线程的调度
状态图:
RUNNABLE:当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,在前面的JVM内存区域划分一篇博文中知道程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
RUNNING:当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
BLOCK:OTHER:sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。
yield方法:调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
-- 摘自:Java并发编程:Thread类的使用 感谢!!!
调度图:
obj.join():是指主线程等待子线程的终止,也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
(为什么要用到join()方法:在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,
但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束)
调用obj的wait(), notify()方法前,必须获得obj锁
描述线程的状态是用一个枚举类型来描述的,严格来讲一个线程有六种状态(具体查看Thread类的源代码),分别是六个枚举值:NEW(新建状态), RUNNABLE(运行状态), BLOCKED(锁池状态),TIMED_WAITING(定时等待状态), WAITING(等待状态), TERMINATED(终止状态),只不过人们平时理解的时候经常会增加阻塞状态,可运行状态,还有挂起状态。
如果是双核系统,那么同一时间点会有 2 条线程处于 Running 状态但是,当线程数大于处理器数时,依然会是多条线程在同一个 CPU 上轮换执行
***************************************************************虚假唤醒*************************************************************************
由于莫名其妙的原因线程有可能在没有调用过notify()和notifyAll()的情况下醒来,这就是所谓的虚假唤醒。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查而不是在if表达式里,这样的一个while循环叫做自旋
while(条件不满足){ this.wait(); } 而不是: if(条件不满足){ this.wait(); }
*******************************************经典例子听说是*************************************************************************************
/* * 但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后, * JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。 * 打印出 10A、10B、10C、9A、9B、9C、8A、8B、8C、7A、7B、7C、6A、6B、6C、5A、5B、5C、4A、4B、4C、3A、3B、3C、2A、2B、2C、1A、1B、1C、 */ public class Main implements Runnable { private String name; private Object prev; // c a b private Object self; // a b c private Main(String name, Object prev, Object self) { this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while (count > 0) { System.out.print(count); // (1)线程pa获取了c对象锁,此时pb,pc线程处于BLOCKED:SYNCHRONIZED状态 // (7)线程pb进来,获取了a对象锁,此时pa线程处于对象c的等待队列,pc线程处于BLOCKED:SYNCHRONIZED状态 // (13)线程pc进来,获取了b对象锁(pa在对象c的等待队列,pb在对象a的等待队列) // (19)只有线程pa不在等待队列(此刻pb在对象a的等待队列,pc在对象b的等待队列) synchronized (prev) { // (2)线程pa获取了a对象锁 // (8)线程pb获取b对象锁 // (14)线程pc获取c对象锁 // (20)....... synchronized (self) { // (3)打印出A // (9)打印出B // (15)打印出C System.out.print(name+"、"); // (4)count变9 // (10)count变8 // (16)count变7 count--; // (5)线程pa调用了对象a的notify()方法,唤醒从对象a的等待队列其中一个的线程(这个时候对象a等待队列实际上是没有线程的)到BLOCKED:SYNCHRONIZED(此时c对象锁还被线程pa控制住) // (11)线程pb调用了对象b的notify()方法,唤醒从对象b的等待队列其中一个的线程(这个时候对象b等待队列上实际上是没有线程的,对象c等待队列上的线程只有pa)到BLOCKED:SYNCHRONIZED(此时a对象锁还被pb控制住) // (17)线程pc调用了对象c的notify()方法,唤醒从对象c的等待队列其中一个的线程(pa在对象c的等待队列,pb在对象a的等待队列)(所以对象c的等待队列的线程pa被唤醒了) // (21)线程pa调用了对象a的notify()方法,唤醒从对象a的等待队列其中一个的线程(唤醒线程pb)(此时线程pc在对象b的等待队列) self.notify(); } // self变量对象锁被释放 try { // (6)线程pa调用了c对象的wait方法,pa释放c对象锁,并进入对象c的等待队列中(此时线程pb,bc争夺c对象锁了)(一定一定要知道,synchronized(){}语句块执行结束,对象锁才释放的) // (12)线程pb调用了对象a的wait方法,pb释放a对象锁,并进入a对象锁的等待队列(pa还在对象c的等待队列,此刻pb在对象a的等待队列) // (18)线程pc释放b对象锁(此刻pb在对象a的等待队列,pc在对象b的等待队列)(所以对象c的等待队列的线程pa被唤醒了,17步被释放了) // (22)线程pa释放锁c,进入对象c的等待队列(线程pa在对象c的等待队列中,pc在b的等待队列中,在a等待队列的线程pb在21步被释放了) prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // prev 变量对象锁被释放 } } public static void main(String[] args) throws Exception { Object a = new Object(); Object b = new Object(); Object c = new Object(); Main pa = new Main("A", c, a); Main pb = new Main("B", a, b); Main pc = new Main("C", b, c); new Thread(pa).start(); Thread.sleep(100); //确保按顺序A、B、C执行 new Thread(pb).start(); Thread.sleep(100); new Thread(pc).start(); Thread.sleep(100); } }
欧巴