以前对于知识,总是习惯有一个模糊的概念,认为只要会用,脑海中知道个大概就可以了,但是却很少系统的深入理解。这不,面试被虐的体无完肤,所以,对于知识,一定要不求甚解 。
上图基本说明了线程的各个状态,但是 sleep wait notify到底是什么意思呢?下面一个一个的说。
1:sleep 顾名思义,线程阻塞,sleep是让线程阻塞,让出CPU的资源,但是此时他是不释放锁的,也就是说在一个synchronized加锁的代码块中,如果sleep,此时这个线程会从Running状态到 Runnable状态,但是代码此时还是阻塞在这里,除非sleep时间到或者调用interrupt打断,那么interrupt又是怎么的原理的?下面慢慢说。所有有时候我们会见到 Thread.sleep(0);其实这个并不是为了让线程阻塞,而且为了调节CPU调用线程的优先级,让sleep的线程让出CPU.
2:wait:wait也是让线程阻塞,让出CPU,但是它和sleep的区别是,他会释放锁。另外,wait是Object的方法,wait有个重载方法,可以设置wait等待时间,wait阻塞可以被notify notifyAll 唤醒,或者被interrupt打断。,
3:notify notifyAll:这两个是唤醒wait的线程,区别是,当某一个代码块有多个阻塞的时候,notify只能唤醒一个,是随机的一个,而notifyAll可以唤醒全部,需要注意的是,唤醒一个和多个等待的线程,但是不会立刻释放锁,而所得释放是看代码的运行情况的。有可能notifyALl后,又slepp,此时wait是不会执行的,因为notify代码块还没释放锁。
到这里不知道你们会不会有个疑问,因为wait和notify都必须在synchronized代码块下面执行,这原因又是什么?
反正我是有这个疑问,然后也是各种找原因,其中有个解释是感觉是很明白的,意思是:假如没有同步代码块,那么wait和notify也没有存在的必要,另外也只有在代码块中,用的是同一个锁对象,才能知道该通知谁应该放弃阻塞。
另外,想了解底层的具体原理,得重虚拟机下手,我不是大牛,不过从一片文章中看到是这样谁的,wait和notify底层也是用JVM的一个监控对象monitor,在HotSpot虚拟机中,monitor采用ObjectMonitor实现。
每个线程都有两个ObjectMonitor对象列表,分别为free和used列表,如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。
ObjectMonitor对象中有两个队列:_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表;_owner指向获得ObjectMonitor对象的线程
waitSet队列存放的是wait的阻塞线程,而EntryList存放的是等待锁的线程(就绪队列)
当我们调用wait的时候,就会把当前线程放进到阻塞队列里面,而当我们调用NotifyAll的时候,就会把阻塞队列的的线程挪到就绪队列。具体的看这个文档https://www.jianshu.com/p/f4454164c017
从上面的原理也可以看出一点注意的事项:wait,和notify本事不是静态方法,需要有一个调用,而这个调用者就是加锁的那个对象,比如下面的是不对的,
4:synchronized:这个是我们常用的锁,它可以用任意一个对象加锁,也可以用任意一个类,在静态方法加的锁是类锁,在普通方法加的锁是对象锁,所有从这里可以看出,锁其实锁的是一块内存区域,静态锁,锁的是类对象所在的内存空间,而对象锁,锁的是这个对象所在的空间地址,
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联(任何对象都可以当成锁),当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁
5:interrupt:Thread本身有三个非常相似得方法,分别如图
其中 interrupt()是中断这个线程,不过有一下注意的地方:
1:该方法只是给线程做一个打断的标记,线程本身不应该不会打算,也不应被外部因数打算,一个线程被打算,应该是线程本身自己做的操作
2:如果执行interrupt()的时候线程已经sleep或者wait,此时会打断阻塞状态,抛出一个异常,我们可以根据这个异常来决定线程该怎么处理,同时会清楚打断状态,也就是说,用isInterrupted()获取的结果是false
isInterrupted()和interrupted()是不同的,前者是非静态方法,需要当前线程调用,而后者是静态方法,作用于当前线程,无需调用者