1. 线程状态图
这是线程的7状态模型图,常见的7大状态之间的转换关系也在上面。多线程之间的通信主要用到4个方法,wait()、wait(long time)、notify()、notifyAll(),其他方法在多线程基础中都有介绍。
-
wait():作用是使当前线程从调用处中断并且释放锁转入等待队列,直到收到notify或者notifyAll的通知才能从等待队列转入锁池队列,没有收到停止会一直死等。
-
wait(long time):相比wait多了一个等待的时间time,如果经过time(毫秒)时间后没有收到notify或者notifyAll的通知,自动从等待队列转入锁池队列。
-
notify():随机从等待队列中通知一个持有相同锁的一个线程,如果没有持有相同锁的wait线程那么指令忽略无效。注意是持有相同锁,并且是随机没有固定的,顺序这一点在生产者消费者模型中很重要,会造成假死的状态。
-
notifyAll():通知等待队列中的持有相同锁的所有线程,让这些线程转入锁池队列。如果没有持有相同锁的wait线程那么指令忽略无效。
-
wait的两个方法都需要注意中断的问题,wait中断是从语句处中断并且释放锁,当再次获得锁时是从中断处继续向下执行。
-
notify 和 notifyAll方法通知是延迟通知,必须等待当前线程体执行完所有的同步方法/代码块中的语句退出释放锁才通知wait线程。
-
这四个方法都必须在获得锁的情况下才能调用,否则会出现非法监视状态异常。
2. 基本使用
2.1、wait立即释放锁 与 notify/notifyAll延迟通知
class MyService{
private Object obj = new Object();
public void waitfun(){
try{
synchronized (obj){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.wait();
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
public void notifyfun(){
try{
synchronized (obj){
System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.notify();
System.out.println("end notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private MyService myService;
public ThreadA(String name, MyService myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.waitfun();
}
}
class ThreadB extends Thread{
private MyService myService;
public ThreadB(String name, MyService myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.notifyfun();
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA a = new ThreadA("Thread", myService);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB("Thread", myService);
b.start();
}
}
分析:a线程调用wait方法进行中断,立即释放锁;main线程睡眠1000ms后,b线程启动获得对象锁开始通知,然后执行.notify语句,最后输出了通知完毕;执行完了b线程同步方法/代码块里的所有语句才进行通知,a线程收到通知进行执行,从中断处继续向下执行最后得到上图结果。
2.2、wait(long time)的自动唤醒
class Service{ }
class ServiceA extends Thread{
private Service service;
public ServiceA(String name, Service service) {
super(name);
this.service = service;
}
@Override
public void run() {
try{
synchronized (service){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
service.wait(5000);
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
System.out.println(Thread.currentThread().getName()+"等待5000ms后自动唤醒!");
} catch (Exception e){
e.printStackTrace();
}
}
}
class ServiceB extends Thread{
private Service service;
public ServiceB(String name, Service service) {
super(name);
this.service = service;
}
@Override
public void run() {
try{
synchronized (service){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
}
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ServiceA a = new ServiceA("ThreadA",service);
ServiceB b = new ServiceB("ThreadB",service);
a.start();
Thread.sleep(100);
b.start();
}
}
方法主体中并没有对wait的线程进行唤醒停止,但是经过time秒后A线程自动唤醒了。wait(long time)方法在经过time(毫秒)之后的等待即使没有收到 通知 会自动从等待队列转入锁池队列。
2.3、提前唤醒,处于wait(long time)的线程如果正处在time等待的时间内提前收到通知会直接进入锁池队列,time时间提前失效。
class Service{ }
class ServiceA extends Thread{
private Service service;
public ServiceA(String name, Service service) {
super(name);
this.service = service;
}
@Override
public void run() {
try{
long begin = 0;
long end = 0;
synchronized (service){
begin = System.currentTimeMillis();
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + begin);
service.wait(8000);
end = System.currentTimeMillis();
System.out.println("end wait: " + Thread.currentThread().getName() + " " + end);
}
System.out.println(Thread.currentThread().getName() + "等待" + (end-begin) + "ms后唤醒!");
} catch (Exception e){
e.printStackTrace();
}
}
}
class ServiceB extends Thread{
private Service service;
public ServiceB(String name, Service service) {
super(name);
this.service = service;
}
@Override
public void run() {
try{
synchronized (service){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
service.notify();
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
}
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ServiceA a = new ServiceA("ThreadA",service);
ServiceB b = new ServiceB("ThreadB",service);
a.start();
Thread.sleep(1000);
b.start();
}
}
2.5、单一通知:notify方法随机的从等待队列中唤醒一个持有相同锁的线程进入锁池队列争抢同步锁,如果没有持有相同锁的wait的线程则notify指令无效。
class MyService{
private Object obj = new Object();
public void waitfun(){
try{
synchronized (obj){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.wait();
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
public void notifyfun(){
try{
synchronized (obj){
System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.notify();
System.out.println("end notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private MyService myService;
public ThreadA(String name, MyService myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.waitfun();
}
}
class ThreadB extends Thread{
private MyService myService;
public ThreadB(String name, MyService myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.notifyfun();
}
}
public class notifyone {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
for(int i = 1;i <= 5;i++){
new ThreadA("Thread"+i, myService).start();
}
Thread.sleep(1000);
for(int i = 6;i <= 12;i++){
new ThreadB("Thread"+i,myService).start();
Thread.sleep(500);
}
}
}
2.6、全部通知:notifyAll方法将等待队列中所有的持有相同锁的线程唤醒进入锁池队列争抢同步锁。
/*
notifyAll全部通知,通知顺序是随机的
由JVM决定,并没有固定顺序
*/
class MyService1{
private Object obj = new Object();
public void waitfun(){
try{
synchronized (obj){
System.out.println("begin wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.wait();
System.out.println("end wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
public void notifyfun(){
try{
synchronized (obj){
System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.notifyAll();
System.out.println("end notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
} catch (Exception e){
e.printStackTrace();
}
}
}
class ThreadA1 extends Thread{
private MyService1 myService;
public ThreadA1(String name, MyService1 myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.waitfun();
}
}
class ThreadB1 extends Thread{
private MyService1 myService;
public ThreadB1(String name, MyService1 myService) {
super(name);
this.myService = myService;
}
@Override
public void run() {
myService.notifyfun();
}
}
public class notifyall {
public static void main(String[] args) throws InterruptedException {
MyService1 myService = new MyService1();
for(int i = 1;i <= 5;i++){
new ThreadA1("ThreadA"+i,myService).start();
}
Thread.sleep(5000);
new ThreadB1("noticeThread",myService).start();
}
}
3. 异常释放锁
学习synchronized时,有几种情况当同步方法 / 块中遇到异常会提前结束线程体。
-
sleep + interrupt:这两个方法没有先后顺序,无论哪个先执行,发生异常时线程体提前中止。
-
手动制造异常:当执行到某个程度的时候,如果想提前结束直接利用throw new Exception()。
-
使用stop方法直接摧毁线程,这个方法已经废弃了不推荐使用。
-
使用return 提前结束线程体的运行,但是这种方法并不是异常。
3.1、手动制造异常提前终止
import java.util.ArrayList;
class MyList{
volatile private ArrayList<String> list = new ArrayList<>(); //保证可见性
public void add(){
list.add("123");
}
public int size(){
return list.size();
}
}
class MyService{
private MyList list = new MyList();
private Object obj = new Object();
public void waitMethod(){
try{
synchronized (obj){
if (list.size() != 5){
System.out.println("begin wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
obj.wait();
System.out.println("end wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
}
} catch (Exception e){
e.printStackTrace();
}
}
public void notifyMethod(){
try{
synchronized (obj){
for(int i = 1;i <= 10;i++){
list.add();
System.out.println("add添加了" + i +"个元素.");
if(list.size() == 5){
obj.notify();
throw new Exception(); //没有异常list中将会有10个元素,制造异常则只有5个
System.out.println("发出通知,等我执行完再执行."); //不会执行
}
}
}
}catch (Exception e){
// e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.notifyMethod();
}
}
class ThreadB extends Thread{
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.waitMethod();
}
}
public class Test01 {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.setName("ThreadA");
ThreadB b = new ThreadB(myService);
b.setName("ThreadB");
b.start();
Thread.sleep(1000);
a.start();
}
}
3.2、wait + interrupt方法提前终止线程
class A{
private Object obj;
public A(Object object) {
this.obj = object;
}
public void method(){
synchronized (obj){
System.out.println("begin wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
}
}
class MyThread extends Thread{
private A a;
public MyThread(A a) {
this.a = a;
}
@Override
public void run() {
a.method();
}
}
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
A a = new A(object);
MyThread myThread = new MyThread(a);
myThread.start();
Thread.sleep(5000);
myThread.interrupt();
}
}
4. 总结
-
sleep睡眠是抱锁睡眠,不会进入等待队列而是进入阻塞状态。如上图
-
wait()方法会立即释放锁中断,进入等待队列。唤醒后仍然需要去争抢获得同步锁才能继续从中断处向下执行。
-
wait(long time)方法会在time毫秒后自动唤醒,如果提前遇到通知会提前进入等待队列。
-
notify / notifyAll 通知是随机顺序通知的,并没有固定的顺序进行通知具体的某个线程。并且只通知持有相同锁的wait线程。
-
灵活使用异常能够帮助线程体的提前终止。