线程协作
package test.thread.coordinate.Object; import java.util.concurrent.TimeUnit; /** * 基本任务 * 任务描述 汽车涂蜡 * 汽车打蜡之后 才可以抛光 而涂蜡任务在涂另一层蜡之前必须在抛光之后 * * @author Administrator * */ public class Car { /** * 打蜡-抛光状态 * false 可以涂蜡 * true 可以抛光 */ private Boolean waxOn = false; /** * flase 是可以打蜡 打蜡之后 修改为true 表示可以抛光 */ public synchronized void waxed() { waxOn = true; this.notifyAll(); } /** * 抛光 true 为可以抛光 抛光完成 修改状态值为false 表示可以打蜡 */ public synchronized void buffed() { waxOn = false; this.notifyAll(); } /** * 检查状态 刚抛光完成状态值为false 若刚抛光完成则是线程挂起等待 打蜡 * 这个应该是在抛光的任务之中负责将不可以抛光的任务线程挂起的 * 由于抛光是在打蜡之后 * @throws InterruptedException */ public synchronized void waitForWaxing() throws InterruptedException { while (waxOn == false){ wait(); } } /** * 检查状态 如果为true 则表示打蜡完成 可以抛光 线程挂起 * 这个是用于在 打蜡中 在打蜡修改标识符后 负责将打蜡线程任务挂起的作用的 * @throws InterruptedException */ public synchronized void waitForBuffing() throws InterruptedException{ while(waxOn == true){ wait(); } } } class WaxOn implements Runnable{ private Car car; public WaxOn(Car c){car = c;} @Override public void run() { try{ while(!Thread.currentThread().isInterrupted()){ System.out.println("开始打蜡!"); TimeUnit.MICROSECONDS.sleep(200); /** * 打蜡 */ car.waxed(); /** * 打蜡之后 必须等待抛光完成才可以 继续打蜡 所以线程挂起 等待 线程抛光 唤醒所有的挂起线程 */ /** * 打蜡结束 等待下一次打蜡 挂起线程 等待抛光 */ car.waitForBuffing(); } }catch(InterruptedException e){ System.out.println("通过中断退出"); } System.out.println("结束打蜡任务"); } } class WaxOff implements Runnable{ private Car car; public WaxOff(Car car) { this.car = car; } @Override public void run() { try{ while(!Thread.currentThread().isInterrupted()){ //检查状态 值是否为false 是则表示刚抛光完成 可以打蜡 不可以则挂起所以在同时开始运行的时候是 抛光任务 先被挂起 等待打蜡结束 System.out.println("抛光开始"); TimeUnit.MICROSECONDS.sleep(200); /** * 抛光开始 修改状态值为false 标记为可以打蜡 并唤醒被挂起打蜡任务的线程 重新加入 争取对象锁的队列 * 然后该任务在次获得对象锁的时候在判断是否可以打蜡 **/ car.waitForWaxing(); car.buffed(); //抛光被挂起 并没有被唤醒 }}catch(InterruptedException e){ System.out.println("通过中断退出"); } System.out.println("抛光结束"); } }
测试类
package test.thread.coordinate.Object; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Waxmatic { public static void main(String[] args) { Car c = new Car(); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new WaxOn(c)); exec.execute(new WaxOff(c)); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { System.exit(0); } finally { exec.shutdownNow(); } } }
WaxOn.run() 方法是给汽车打蜡的第一个步骤, 首先sleep将该任务阻塞 模拟打蜡的过程,然后 调用打蜡方法 将打蜡-抛光标识修改为true ,并且调用watiForBuffing(),这个方法就是检查标识是否可以继续可以打蜡 也就是检查标识 为刚打蜡结束时也就是 标识为true 就会挂起当前线程。并唤醒其他该对象上被挂起的所有线程 在这里其实就是唤醒抛光的线程
然后该线程一直等到 有其他方法唤醒。
然后线程WaxOff.run()方法 是相当于第二个步骤 因为线程是同步运行,虽然是加了同步控制块 但是 当WaxOff.run方法 若抢先一步抢占对象锁在车辆还没有打蜡的时候就被执行 ,但是这样是不符合逻辑的,为了防止这种情况 把在抛光结束之后 标识符被修改以后 才调用的检查标识符看是否可以继续抛光的方法 提前放到 没抛光之前进行检查 ,若可以抛光则进行抛光,然后while 循环回来 再次检查标识符状态,若不可以继续抛光则线程被挂起 并唤醒所有挂起线程 其实就是唤醒了 打蜡被挂起的任务线程
我们再利用wait 和 notify 或者notifyAll 这种方式进行线程之间的协作,有可能因为 线程之间的切换 或者没有进行同步彻底的 时候 就会反生 死锁
例如
T1
sychronized(sharedMonitor){
<setup condition for T2>(这代表防止T2调用wait的一个动作,当然前提是T2还没有被挂起)
sharedMonitor.notifyAll();
}
T2
while(someCondition){
// Point1
sychronized(someCondition){
sharedMonitor.wait();
}
}
假设T2 发现someCondition 为true 他开始执行下面的代码 但是在T1时 线程调度器 将执行线程切换为T1 T1中先一步执行唤醒方法,待到T2得以继续执行时 他不会知道T1已经没有唤醒他的机会了 他还是会进入 wait 然后陷入无限的等待 发生死锁
正确的T2应该是避免somecondition被竞争
sychronized(sharedMonitor){
while(someCondition){}
sharedMonitor.wait();
}
}