java编程思想笔记-并发之线程协作(一)

来源:互联网 发布:电脑数据怎么恢复 编辑:程序博客网 时间:2024/06/09 18:22

1.wait和notifyAll简单使用
1.通过wait使当前线程挂起,并释放当前线程持有的锁,而sleep和yield方法则不会释放自己拥有的锁
2.通过notify()或者notifyAll使wait的时间到期
3.wait(),notifyAll(),notify()方法都是基类的一部分而不属于线程
4.wait(),notifyAll(),notify()在调用前必须获取对象锁,否则虽然程序能够通过编译,但是运行时候会得到IllegalMonitorStateException异常

汽车打蜡与抛光的例子:
汽车每打一次蜡必须抛光一次,而抛光不能发生在打蜡之前

package com.tij.thread.cooperate;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;class Car{    private boolean waxOn=false;    public synchronized void waxed(){        waxOn=true;        notifyAll();    }    public synchronized void buffed(){        waxOn=false;        notifyAll();    }    //必须让线程获取锁线程才能wait    //打蜡之前通过waxOn不停的检查是否已经经过抛光    public synchronized void waitForWaxing() throws InterruptedException {    //一定要使用循环检查,避免其它线程再次改变了条件而当前线程不知道        while (!waxOn) {            wait();        }    }    //必须让线程获取锁线程才能wait    //抛光之前通过waxOn不停的检查是否已经经过打蜡    public synchronized void waitForBuffing()throws InterruptedException{    //一定要使用循环检查,避免其它线程再次改变了条件而当前线程不知道        while (waxOn) {            wait();        }    }}class WaxOn implements Runnable{    private Car car;    public WaxOn(Car car){        this.car=car;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                    System.out.println(" Wax On ");                    TimeUnit.MILLISECONDS.sleep(200);                    car.waxed();                    car.waitForBuffing();            }        } catch (InterruptedException e) {            System.out.println("WaxOn Exiting via interrupt ");        }        System.out.println(" Ending Wax On task ");    }}class WaxOff implements Runnable{    private Car car;    public WaxOff(Car car){        this.car=car;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                car.waitForWaxing();                System.out.println(" Wax Off ! ");                TimeUnit.MILLISECONDS.sleep(200);                car.buffed();            }        } catch (Exception e) {            System.out.println("WaxOff Exiting via interrupt ");        }        System.out.println(" Ending Wax Off task ");    }}public class WaxOMatic {    public static void main(String[] args) throws InterruptedException {        Car car=new Car();        ExecutorService exec=Executors.newCachedThreadPool();        exec.execute(new WaxOff(car));        exec.execute(new WaxOn(car));        TimeUnit.SECONDS.sleep(5);        exec.shutdownNow();    }}

一开始没有按照Thinking In Java里面的代码,我对WaxOn类进行了轻微的改动,将try,catch放到了循环的外面我认为这样的改动与Thinking In Java里面的代码没什么区别,代码如下

class WaxOn implements Runnable{    private Car car;    public WaxOn(Car car){        this.car=car;    }    @Override    public void run() {        while (!Thread.interrupted()) {            try {                System.out.println(" Wax On ");                TimeUnit.MILLISECONDS.sleep(200);                car.waxed();                car.waitForBuffing();            } catch (InterruptedException e) {                System.out.println("WaxOn Exiting via interrupt ");            }        }        System.out.println(" Ending Wax On task ");    }}

实际上这样的改变有潜在的问题,这会导致WaxOn任务始终无法退出循环,我们将WaxOn的睡眠时间调到2000ms来验证这一猜想,这个时候main线程发出shutdownNow()命令关闭线程池内所有的线程,但是WaxOn正在睡眠所以抛出异常,因为内部try-catch捕获了该异常,并在捕获中自动修改了interrupt标志为false,所以导致WaxOn始终进行循环

上面的例子使用的是线程机制进行协作,也可以不依赖于线程机制进行协作,如下面这个例子利用flag标记进行通信:

public class MessageTest {    private static volatile boolean flag;    private static int spins;    public static void main(String[] args) throws InterruptedException {        Runnable r1 = new Runnable() {            @Override            public void run() {                while (true) {                    try {                        TimeUnit.MILLISECONDS.sleep(10);                    } catch (Exception e) {                        return;                    }                    flag = true;                    //System.out.println("set flag ---------------->"+flag);                }            }        };        Runnable r2 = new Runnable() {            @Override            public void run() {                while (true) {                    while (!flag && !Thread.currentThread().isInterrupted()) {                        spins++;                    }                    System.out.println("Spun " + spins + " times");                    spins = 0;                    flag = false;                    if (Thread.interrupted()) {                        return;                    }                }            }        };        ExecutorService exec = Executors.newCachedThreadPool();        exec.execute(r1);        exec.execute(r2);        TimeUnit.SECONDS.sleep(10);        exec.shutdownNow();    }}

2.notify()/wait()错失信号造成的死锁

T1synchronized(shareMonitor){    <setup condition for T2>    sharedMonitor.notify();}T2while(someCondition){    synchronized(shareMonitor){        sharedMonitor.wait();    }}T2首先运行经过计算得出someCondition满足循环条件,线程调度器回到T1,T1通过<setup condition for T2>设置T2 someCondition为false,但是太晚T2已经进入了循环,同时T2继续运行notify(),并释放锁,T2得到锁并进入等待,但是notify已经错失,便造成了死锁,通过以下方法解决问题synchronized(shareMonitor){    //务必通过while来判断条件,避免错失条件改变    while(someCondition){        sharedMonitor.wait();    }}自此我们得到的启示是    1.调用notify的时候要仔细思考是否能通知到目标线程    2.wait前用while循环进行条件判断是否进行等待
原创粉丝点击