线程之间的协作

wait() notify() notifyAll()

wait() Causes the current thread to wait until it is awakened, typically by being notified or interrupted.

notify() Wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is
chosen to be awakened. The choice is arbitrary and occurs at the
discretion of the implementation. A thread waits on an object’s
monitor by calling one of the wait methods. The awakened thread will
not be able to proceed until the current thread relinquishes the lock
on this object. The awakened thread will compete in the usual manner
with any other threads that might be actively competing to synchronize
on this object; for example, the awakened thread enjoys no reliable
privilege or disadvantage in being the next thread to lock this
object.
This method should only be called by a thread that is the owner of
this object’s monitor. A thread becomes the owner of the object’s
monitor in one of three ways:
By executing a synchronized instance method of that object.
By executing the body of a synchronized statement that synchronizes on
the object.
For objects of type Class, by executing a synchronized
static method of that class. Only one thread at a time can own an
object’s monitor.

notifyAll() Wakes up all threads that are waiting on this object’s monitor. A
thread waits on an object’s monitor by calling one of the wait
methods. The awakened threads will not be able to proceed until the
current thread relinquishes the lock on this object. The awakened
threads will compete in the usual manner with any other threads that
might be actively competing to synchronize on this object; for
example, the awakened threads enjoy no reliable privilege or
disadvantage in being the next thread to lock this object.

This method should only be called by a thread that is the owner of
this object’s monitor. See the notify method for a description of the
ways in which a thread can become the owner of a monitor.

总之,wait()方法调用后该线程将挂起,同时释放它所含的锁。
notify()调用时,正在等待的线程将被唤醒,如果此线程可以获得锁,它将开始执行。如果正在等待线程有多个,就随机唤醒一个。notifyAll()则是唤醒所有等待的线程,这里的所有指的是等待对应对象的线程。

初识

wait与notify经常在一起使用 下面看代码分析 wait和notify要在同步代码段中 线程共有一个Car,一个线程对Car操作时,此线程得到锁,其他线程不能执行,也就不会执行到wait()方法,也就不会出现两个都在等待的情况
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//线程协作,A线程执行一次,B线程执行一次
//唤醒 必须获得锁之后才会被唤醒
//wait notify必须在同步代码段中使用
//还可以使用lock()和condition.await()
//Lock.newCondition()产生一个condition对象
class Car{
    private boolean waxOn = false;
    public synchronized void waxed(){
        waxOn=true;

        notifyAll();
    }
    public synchronized void buffed(){
        waxOn = false;
        notifyAll();
    }
    public synchronized void waitForWaxing()
            throws InterruptedException{
        while(waxOn==false)
            wait();
    }
    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.interrupted()){
                System.out.println("Wax On! ");
                TimeUnit.MILLISECONDS.sleep(200);
                car.waxed();
                car.waitForBuffing();
            }
        }catch (InterruptedException e){
            System.out.println("Exiting via interrupt!");
        }
        System.out.println("Ending Wax On task");
    }
}
class WaxOff implements Runnable{
    private Car car;
    public WaxOff(Car c){
        car = c;
    }
    @Override
    public void run(){
        try{
            while(!Thread.interrupted()){
                System.out.println("Wax Off! ");
                TimeUnit.MILLISECONDS.sleep(200);
                car.buffed();
                car.waitForWaxing();
            }
        }catch (InterruptedException e){
            System.out.println("Exiting via interrupt!");
        }
        System.out.println("Ending Wax Off task");

    }
}
public class WaxOMatic {
    //线程共有一个Car,一个线程对Car操作时,此线程得到锁,其他线程不能执行,不会出现两个都在等待的情况
    public static void main(String...args)throws Exception{
        Car car = new Car();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new WaxOff(car));
        exec.execute(new WaxOn(car));
        TimeUnit.SECONDS.sleep(5);
        exec.shutdownNow();//会对所有线程调用interrupt()方法
    }
}

notifyAll()因为某个特点锁调用时唤醒等待该锁的所有线程,并不是所有线程
下面代码的Task与Task2一样,里面的Blocker是不同对象,被唤醒时输出当前线程名字,只对Task调用notifyAll()或notify(),发现Task2没有被唤醒

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

//等待特定锁的才会被唤醒
//该程序的Task被唤醒,但是Task2没有被唤醒
//notifyAll也只会唤醒特定线程
class Blocker{
    String s;
    Blocker(String s){
        this.s=s;
    }
    synchronized void waitingCall(){
        try{
            while(!Thread.interrupted()){
                wait();
                System.out.print("<"+s+ ">"+ Thread.currentThread()+" ");
            }
        }catch(InterruptedException e){

        }
    }
    synchronized void prod(){notify();}
    synchronized void prodAll(){notifyAll();}
}
class Task implements Runnable{
    static Blocker blocker = new Blocker("Task");
    @Override
    public void run() {
        System.out.println("Task");
        blocker.waitingCall();
    }
}
class Task2 implements Runnable{
    static Blocker blocker = new Blocker("Task");

    @Override
    public void run() {
        System.out.println("Task2");
        blocker.waitingCall();
    }
}
public class NotifyVSNotifyAll {
    public static void main(String...args) throws Exception{
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0;i<5;i++)
            exec.execute(new Task());
        exec.execute(new Task2());
        Timer timer = new Timer();
        /*
        command:执行线程
        Delay:初始化延时
        period:两次开始执行最小间隔时间
        */
        timer.scheduleAtFixedRate(new TimerTask() {
            boolean prod = true;
            @Override
            public void run() {
                //Task2没有被唤醒
                if(prod){
                    System.out.println("\nnotify()");
                    Task.blocker.prod();
                    prod=false;
                }else{
                    System.out.println("\nnotifyAll()");
                    Task.blocker.prodAll();
                    prod=true;
                }
            }
        },400,400);
        TimeUnit.SECONDS.sleep(5);
        timer.cancel();
        System.out.println("\nTimer canceled");
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.print("Task.blocker.prodAll()");
        Task2.blocker.prodAll();
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.println("\nShutting down");
        exec.shutdownNow();
    }
}

生产者与消费者

下面展现了一种使用方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

//该程序避免了notify执行时没有wait中的线程,导致阻塞
//先锁自己,执行完wait时另一个线程必须获得这个锁才能进行notify
class Meal{
    private final int orderNum;
    public Meal(int orderNum){
        this.orderNum = orderNum;
    }
    public String toString(){
        return "Meal "+orderNum;
    }
}
/*顾客*/
class WaitPerson implements Runnable {
    private Restaurant restaurant;

    public WaitPerson(Restaurant r) {
        restaurant = r;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    while (restaurant.meal == null)
                        wait();
                }
                System.out.println("Waitperson got " + restaurant.meal);
                //得到chef的锁
                synchronized (restaurant.chef) {
                    restaurant.meal = null;
                    //吃完后提醒厨师,此时chef一定执行完wait
                    restaurant.chef.notifyAll();
                }
            }
        } catch (InterruptedException e) {
            System.out.println("WaitPerson interrupted");
        }
    }
}
/*厨师*/
class Chef implements Runnable{
    private Restaurant restaurant;
    private int count = 0;
    public Chef(Restaurant r){
        restaurant = r;
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                synchronized (this){
                    //若有meal 等待
                    while(restaurant.meal!=null)
                        wait();
                }
                if(++count == 10){
                    System.out.println("Out of food, closing");
                    restaurant.exec.shutdownNow();
                }
                System.out.println("order up!");
                //得到person的锁,此时的person一定执行完wait,才能得到锁
                synchronized (restaurant.waitPerson){
                    restaurant.meal = new Meal(count);
                    //做好后提醒顾客
                    restaurant.waitPerson.notifyAll();
                }
                //sleep方法会检测异常状态
                //删掉这句话将不会产生异常
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch (InterruptedException e){
            System.out.println("Chef interrupted");
        }
    }
}
public class Restaurant {
    Meal meal;
    ExecutorService exec = Executors.newCachedThreadPool();
    WaitPerson waitPerson = new WaitPerson(this);
    Chef chef = new Chef(this);
    public Restaurant(){
        exec.execute(chef);
        exec.execute(waitPerson);
    }
    public static void main(String...args){
        new Restaurant();
    }
}

队列

take方法和put方法 更加简单的实现线程协作
这里只讨论简单用法
LinkedBlockingQueue无界 ArrayBlockingQueue有界
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.*;

//生产者-消费者队列
//put(), take()
class LiftOffRunner implements Runnable{
    private BlockingQueue<LiftOff> rockets;
    public LiftOffRunner(BlockingQueue<LiftOff> queue){
        rockets=queue;
    }
    public void add(LiftOff lo){
        try{
            rockets.put(lo);
        }catch (InterruptedException e){
            System.out.println("interrupted during put()");
        }
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                LiftOff rocket = rockets.take();
                rocket.run();
            }
        }catch (InterruptedException e){
            System.out.println("Waking from take()");
        }
        System.out.println("Exiting LiftOffRunner");
    }
}
public class TestBlockingQueues {
    static void getKey() {
        try{
            new BufferedReader(new InputStreamReader(System.in)).readLine();
        }catch (IOException e){
            throw new RuntimeException(e);
        }
    }
    static void getKey(String message){
        System.out.println(message);
        getKey();
    }
    static void test(String msg,BlockingQueue<LiftOff> queue){
        System.out.println(msg);
        LiftOffRunner runner = new LiftOffRunner(queue);
        Thread t = new Thread(runner);
        t.start();
        for(int i = 0;i<5;i++)
            runner.add(new LiftOff(5));
        getKey("Press 'Enter' (" + msg+")");
        t.interrupt();//队列为空时将阻塞,等待新的LiftOff加入,程序不会终止,调用interrupt将其打断
        System.out.println("Finished "+msg+" test");
    }
    public static void main(String...args){
        test("LinkedBlockingQueue",
                new LinkedBlockingQueue<LiftOff>());
        test("ArrayBlockingQueue",
                new ArrayBlockingQueue<LiftOff>(3));
        test("SynchronousQueue",
                new SynchronousQueue<LiftOff>());
    }
}

public class LiftOff implements Runnable{
    protected int downCount = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    public LiftOff(){}
    public LiftOff(int count){
        downCount = count;
    }
    public String status(){
        return "#" + id + "(" +(downCount>0?downCount:"LiftOff!") + ")";
    }

    @Override
    public void run() {
        while(downCount-->0){
            System.out.println(status());
            try{
                //Thread.sleep(1000);//线程休眠  阻塞 单位毫秒 1000毫秒 及一秒
                //现在的写法
                TimeUnit.MILLISECONDS.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

比如一个吐司店,多个队列共同执行,队列为空的时后将会阻塞,队列一旦有新元素添加,调用take()方法返回。
刚开始黄油队列和果酱队列都为空,先制作吐司
同时下面也是个典型的枚举用法

import java.util.Random;
import java.util.concurrent.*;
//吐司队列制作吐司->传入黄油队列抹黄油->传入果酱队列抹果酱->传入(finishedQueue)Eater
//3个队列被所有的类操作共有,队列里面为空的时候线程阻塞
class Toast{
    public enum Status{DRY,BUTTERED,JAMMED}
    private Status status = Status.DRY;
    private final int id;
    public Toast(int idn){
        id=idn;
    }
    public void butter(){
        status = Status.BUTTERED;
    }
    public void jam(){
        status = Status.JAMMED;
    }
    public Status getStatus(){
        return status;
    }
    public int getId(){
        return id;
    }

    @Override
    public String toString() {
        return "Toast "+id+":"+status;
    }
}
class ToastQueue extends LinkedBlockingQueue<Toast>{}
/*制作吐司*/
class Toaster implements Runnable{
    private ToastQueue toastQueue;
    private int count = 0;
    private Random rand = new Random(47);
    public Toaster(ToastQueue tq){
        toastQueue=tq;
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                TimeUnit.MILLISECONDS.sleep(100+rand.nextInt(500));
            Toast t = new Toast(count++);
            System.out.println(t);
            toastQueue.put(t);
            }
        }catch (InterruptedException e){
            System.out.println("Toaster interrupted");
        }
        System.out.println("Toaster off");
    }
}
/*涂黄油*/
class Butterer implements Runnable{
    private ToastQueue dryQueue,butteredQueue;
    public Butterer(ToastQueue dry,ToastQueue buttered){
        dryQueue = dry;
        butteredQueue = buttered;
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                Toast t = dryQueue.take();
                t.butter();
                System.out.println(t);
                butteredQueue.put(t);
            }
        }catch (InterruptedException e){
            System.out.println("Butterer interrupted");
        }
        System.out.println("Bufferer off");
    }
}
/*抹果酱*/
class Jammer implements Runnable{
    private ToastQueue butteredQueue ,finishedQueue;
    public Jammer(ToastQueue butteredQueue,ToastQueue finishedQueue){
        this.butteredQueue=butteredQueue;
        this.finishedQueue=finishedQueue;
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                Toast t = butteredQueue.take();
                t.jam();
                System.out.println(t);
                finishedQueue.put(t);
            }
        }catch (InterruptedException e){
            System.out.println("Jammer interrupted");
        }
        System.out.println("Jammer off");
    }
}
class Eater implements Runnable{
    private ToastQueue finishedQueue;
    private int counter = 0;
    public Eater(ToastQueue tq){
        finishedQueue=tq;
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                Toast t = finishedQueue.take();
                if(t.getId()!=counter++||t.getStatus()!=Toast.Status.JAMMED){
                    System.out.println(">>>> Error: " +t);
                    System.exit(1);
                }else
                    System.out.println("Chomp! "+t);
            }
        }catch (InterruptedException e){
            System.out.println("Eater interrupted");
        }
        System.out.println("Eater off");
    }
}
public class ToastOMatic {
    public static void main(String...args)throws Exception{
        ToastQueue dryQueue = new ToastQueue(),butteredQueue = new ToastQueue()
                ,finishedQueue = new ToastQueue();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Toaster(dryQueue));
        exec.execute(new Butterer(dryQueue,butteredQueue));
        exec.execute(new Jammer(butteredQueue,finishedQueue));
        exec.execute(new Eater(finishedQueue));
        TimeUnit.SECONDS.sleep(5);
        exec.shutdownNow();
    }
}

任务间使用管道进行输入输出

与其他read()方法不同,PipedReader是可以中断的
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//PipedReader可中断
class Sender implements Runnable{
    private Random rand = new Random(47);
    private PipedWriter out = new PipedWriter();
    public PipedWriter getPipedWriter(){
        return out;
    }

    @Override
    public void run() {
        try{
            while(true){
                for(char c = 'A';c<='z';c++){
                    out.write(c);
                    TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
                }
            }
        }catch (IOException e){
            System.out.println(e+"Sender Writer exception");

        }catch (InterruptedException e){
            System.out.println(e+"Sender sleep interrupted");
        }
    }
}
class Receiver implements Runnable{
    private PipedReader in;
    public Receiver(Sender sender)throws IOException{
        in = new PipedReader(sender.getPipedWriter());
    }

    @Override
    public void run() {
        try{
            while(true){
                System.out.println("read "+(char)in.read()+".");
            }
        }catch (IOException e){
            System.out.println(e + "Receiver read exception");
        }
    }
}
public class PipedIO {
    public static void main(String...args)throws Exception{
        Sender sender = new Sender();
        Receiver receiver = new Receiver(sender);
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(sender);
        exec.execute(receiver);
        TimeUnit.SECONDS.sleep(4);
        exec.shutdownNow();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值