多线程

1.什么进程?
* 值得就是正在运行的程序,是系统进行资源分配和调用的独立单位
* 每一个进程都有它自己的内存空间和系统资源

特征:

1、独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间

2、动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的

3、并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

2.什么线程?
* 是进程中的每个顺序控制流,是一条执行路径
* 一个进行如果只有一条执行流程,则为单线程
* 一个进程有多条执行路径,则为多线程(多线程优点:

1、进程之间不能共享内存,但线程之间共享内存非常容易。

2、系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高

3、Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程)

* 3.什么是并行?什么是并发?
* 前者是逻辑上同时发生,指在某一个时间内同时运行的多个程序
* 后者是物理上同时发生,指在某一个时间内同时运行多个程序
* 4.java的运行原理?
* 由java命令启动JVM,JVM启动就相当于启动了一个进程
* 接着该进程创建了一个主线程去调用main()方法
* 5.需求:我们要实现多线程的一个程序,问 我们要如何进行实现?
* 由于线程是依赖进程而 存在的,所以我们应该先创建一个进程出来,
* 而进程是由系统进行创建的,所以我们应该去调用系统工程,创建一个进程
* java是不能直接去调用系统功能的,所以我们没办法直接实现多线程的程序
* 但是呢,java可以去调用C/C++写好的程序来实现多线程的
由C/C++去调用系统功能创建进程,然后由java去调用这个方法

1.继承Thread类
步骤:
1.自定义MyThread,继承Thread类
2.在MyThread中重写run()方法(该run方法的方法体就代表了线程需要执行的任务)
为什么要重写run()方法?
不是类中的所有代码都需要被线程去执行的.
而这个时候呢,为了区分哪些代码能够被线程执行。java中提供了Thread类中的run()
用来包含哪些被线程执行的代码
3.创建对象(创建Thread类的实例)
4.启动线程(调用线程的start()方法来启动线程 )

run() 他是一个单线程,直接可以当做普通方法去使用,
如果我想 执行一个多线程 那么另一个方法 start()

线程的生命周期:

新建和就绪状态:

当程序使用new关键字创建一个线程后,该线程就处于新建状态。

当线程对象调用了start()方法后,该线程就处于就绪状态。

运行和阻塞状态:

如果处于就绪状态的线程获取了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。

当线程调用sleep(),调用一个阻塞式IO方法,线程会被阻塞

死亡状态:

1、run()或者call()方法执行完成,线程正常结束

2、线程抛出一个未捕获的Exception或Error

3、直接调用该线程的stop方法来结束该线程——该方法容易导致死锁,不推荐使用

 创建线程的三种方法:

采用Runnable、Callable接口的方式创建多线程的优缺点:

优点:

1、线程类只是实现了 Runnable接口或 Callable接口,还可以继承其他类

2、在这种方式下,多个线程可以共享同一个 target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

缺点:

编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。

采用继承 Thread类的方式创建多线程的优缺点:

优点:

编写简单,如果需要访问当前线程,则无须使用 Thread.current Thread()方法,直接使用this即可获得当前线程

缺点:

因为线程已经继承了Thread类,所以不能再继承其他类

start()和run的区别?

run()仅仅是封装线程执行的代码,直接调用时可以是 一个普通的方法
start()首先启动了进程,然后由jvm去调用该线程的run()方法

// Thread t = new Thread();
// t.start();
// t.start();
//报错:java.lang.IllegalThreadStateException 非法的线程状态异常
//为什么会产生这个错误
// 是这个相当于t线程被调用了两次,而不是两个线程启动

虽然这个线程被调用了两次,可是呢 我还不知道到底是哪个线程去执行的,
获取和设置线程的名称

Thread类的基本获取和设置线程的方法:

1.public final String getName() 获取线程的对象名称
2.public final void setName(String name):设置线程的名称
我们也可以通过构造方法去给线程起名称
问:

如果获取了main方法所在的线程名称?
例如:针对如果不是Thread类的子类中如何获取线程名称对象呢?
1public static Thread curretThread() 返回当前正在执行的线程名称
2.Thread.curretThread().getName()

我们要获取main()方法 所在的线程对象名称  如何解决?

//遇到这种情况呢,Thread类提供了一个很好玩的方法
1.public static Thread currentThread() 返回当前正在执行的线程对象

线程的调度
假如我们的计算机只有一个cpu,那么CPU在某一个时刻只能执行一条指令
线程只有得到CPU时间片才能有使用权,才能执行指令
线程的两种调度策略
1.分时调度策略: 所有线程轮流的去使用CPU的使用权,平均进行分配,每个线程占用CPU的时间
2.抢占式调度策略:优先让级别高的线程使用CPU,如果线程的优先级相同,那么他们会随机选择一个,优先级高的线程获取CPU
时间片的调度策略会多一些.
注意:java中用的就是抢占式调度模型

我们的线程是没有设置优先级的,肯定是有默认的优先级

在java中默认的优先级是5
线程优先级的范围:1-10
线程优先级仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看见更好的效果

设置线程优先级的方法?

public final  void serPRriority(int NewPriority) : 更改线程对的优先级

线程的睡眠:

方法:
public static void sleep(long milis):在指定的毫秒数内让当前正在执行的线程休眠(暂停一会再去执行)
注意:
在sleep中 传入值是毫秒值

线程的加入:

方法:
1.public final void join():等待该线程或者终止
大体的概念:
我调用了这个方法,必须线程执行完毕之后,其他的线程才能调用

线程的中断与结束:

方法:
public final stop():让线程停止,过时间了,但是还可以使用
public void interrupt():中断线程,把线程装填停止,并抛出一个INTERRUPTEDEXCEPTION

创建线程的另一种方法  :

Runnable接口的类,该类也会去实现run()方法,然后可以分配该类的实例,
实现Runnable接口的好处:
1.可以避免由于JAVA当继承带来的局限性
2.适合多个相同的程序的代码去处理同一个资源的问题,把线程同程序的代码、数据有效的进行分离,较好的体现了面向对象的设计思想

实现Runable接口的步骤

1.定义一个MyRunnable,实现Runable接口
2.在MyRunnable中重写run()方法
3.创建MyRunnable类的对象
4.创建Thread类的对象,把MyRunnable对象作为构造方法的参数
5.启动线程

使用Runnable接口的好处:不影响MyRunnable继承其他的类

同步锁的弊端

1.效率比较低
2.容易产生死锁

例如:
两个或者两个以上的线程在争夺资源的过程中,发生了一种互相等待的现象

(举例:

中国人、美国人吃饭案例
正常情况下: 中国人:一双筷子

美国人:一个刀一个叉

现在的问题:
中国人:一只筷子 一个刀
美国人:一只筷子 一个叉

线程之间的通信:

针对同一个资源的操作有不同种类的类型
只出不进:电影院卖票(八佰、金刚川)
可进可出:早餐店(生产者:厨师 消费者:顾客)

Lock锁

背景 :
虽然我们可以理解同步代码块和同步方法的所对象,但是我们并没有看见那块上了锁,并在哪里释放了锁
为了更好更清晰的表达如何并如何释放锁,JDK5之后提供了一个新的锁对象,LOCK
lock是接口不能直接序列化,这里采用他的实现类ReentrantLock来实例化
方法ReentrantLock创建一个ReentrantLock的实例
加锁解锁的方法:
方法名 说明
void lock() 获得锁
void unlock() 释放锁

线程池

线程池的基本背景
程序启动一个新的线程成本是很高的,因为它涉及到与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是
当程序中要创建大量的生存很短的线程时,就要考虑使用线程池
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中称为空闲状态,等待下一个对象来使用
java内置支持线程池
线程是使用进程的资源,所以每次开启一个线程,它的成本是比较高的,所以使用线程池,就能解决这个问题
如何使用线程池?
Executors工厂来产生线程池,如一下几个方法
1.public static ExecutorService newCachedThreadPool()开启具有缓存功能的线程池
2.public static ExecutorService newFixedThreadPool(int nThreads) 创建爱你多少个线程池
3.public static ExecutorService newSingleThreadExecutor()创建单个的线程池

这些方法的返回值是ExecutorService对象,该对象表示一个线程池..可以执行Runnable对象或者Callable对象代表的线程.

步骤:

1.创建线程池对象
2.创建Runnable实例
3.提交Runnable实例
4..关闭线程池

线程池的好处

线程池里面的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲装填,等待下一个对象来使用

实现Callable接口

好处:

1.可以有返回值
2.可以抛出异常

弊端:

代码比较复杂,所以一般不用

原文地址:https://www.cnblogs.com/mrr19990327/p/14204885.html