Java多线程并发
Table of Contents
1.Java并发知识库
2.Java线程实现/创建方式
2.1 继承Thread类。
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。
public class MyTread extends Thread{
public void run(){
sout("run");
}
}
MyTread myt = new MyTread();
myt.start();
2.2 实现Runnable接口
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
//启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread 实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
//事实上,当传入一个Runnable target 参数给Thread 后,Thread 的run()方法就会调用
target.run()
public void run() {
if (target != null) {
target.run();
}
}
3.4种线程池
Java里面线程池的顶级接口时Executor,但是严格意义上讲,Executor并不是一个线程池,只是一个执行线程的工具,真正的线程池接口是ExecutorService.
3.1 newCachedThreadPool
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用他们,对于执行很多短期异步任务的程序而言,这些线程池通常可以提高程序性能。
3.2 newFixedThreadPool
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
3.3 newScheduledThreadPool
创建一个线程池,可安排在给定延迟后运行命令或者定期地执行。
3.4 newSingleThreadExcutor
返回一个线程池,这个线程池可以在线程死后,重新启动一个线程来替代原来的线程继续执行下去。
4.线程生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU 独自
运行,所以CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
4.1 新建状态 New
当程序使用关键字new创建了一个线程之后,线程就处于新建状态, 由JVM为其分配内存,并初始化其成员变量的值。
4.2 就绪状态 Runnable
当线程对象调用start方法之后,该线程就处于就绪状态,JVM会为其创建方法调用栈和程序计数器,等待调度运行。
4.3 运行状态 Running
如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,该线程处于运行状态。
4.4 阻塞状态 Blocked
阻塞状态是指线程因为某种原因放弃了CPU的使用权,即让出了cpu时间片,暂时停止运行,直到线程进入可运行状态,才有机会再次获得时间片,转到运行状态。
4.4.1 等待阻塞。
运行running 的线程执行wait()方法,JVM会把该线程放入等待队列。
4.4.2 同步阻塞
运行running 的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池lock pool中。
4.4.3 其他阻塞
运行running的线程执行Thread.sleep 或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态,当sleep()状态超时,join()等待线程终止或者超时、或者IO处理完毕时,线程重新转入可运行状态
5.终止线程4种方式
5.1 正常运行结束。
5.2 使用退出标志退出线程。
一般run()方法执行完,线程就会正常结束。然而有些线程需要长时间运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,如boolean,设置true或者false控制while循环是否退出。
public class ThreadSafe extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit){
//do something
}
}
}
定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false,在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值。
另:volatile
5.3 Interrput方法结束线程
1.线程处于阻塞状态,如使用了sleep,同步锁的wait,socket种的receiver,accept等方法时,会使线程处于阻塞状态,当调用线程的interrupt方法时,会抛出InterruptException异常,阻塞种的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态, 从而让我们有机会结束这个线程。通常很多人认为只要调用interrupt 方法线程就会结束,实际上是错的, 一定要先捕InterruptedException 异常之后通过break 来跳出循环,才能正常结束run 方法。
2.线程未处于阻塞状态:使用isInterrupted判断线程的中断标志来退出循环,当使用interrupt方法时,中断标志就会置true。和使用自定义的标志来控制循环。
5.4stop方法终止线程(线程不安全)
程序中可以直接使用thread.stop来强行终止线程,但是很危险,就像突然关机,不安全主要是thread.stop调用之后,创建子线程的线程会抛出ThreadDeatheror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用
thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用stop 方法来终止线程。