线程的基本协作机制

上一篇文章主要讲述了线程的基本概念以及线程中常用的方法

接下来讲述线程中的基本协作机制:

     1.生产消费模型;

     2.同时开始;

     3.等待结束;

     4.集合点。

首先介绍Object类中的wait/notify方法:

public  final  void  wait()  throws  InterruptedException;

public  final  void  wait(long timeout)  throws  InterruptedException;

上述的两个方法中一个带参数,一个不带参数。

带时间参数,单位是毫秒,表示最多等待这么长的时间,参数为0表示无限期等待;

不带时间参数,表示无限期等待;

除了用于锁的等待队列,每个线程还有另一个等待队列,表示条件队列;当调用wait方法就会把当前线程放入该队列中,他需要等待一个条件,这个太条件他自己改变不了,需要其他线程改变吗。

当线程在等待的过程中被中断,会抛出异常(参考上一篇文章);

public  final  native  void  notify();

public  final  native  void  notifyAll();

当线程在调用wait方法之后,需要等待一个条件,当其他条件改变后,需要调用notify方法;

notify就是从条件队列中选中一个线程将其从队列移除并唤醒;

notifyAll会移除所有的线程并唤醒它们。

看下面的例子:

public class Main extends Thread  {

    private  volatile boolean flag = false;
    @Override
    public void run() {
        try {
            synchronized (this) {
                while (!flag) {
                    wait();
                }
            }
            System.out.println("flag");
        }catch (InterruptedException e){

        }
    }
    public  synchronized void setFlag(){
        this.flag = true;
        notify();
    }
    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.start();
        Thread.sleep(1000);
        System.out.println("flag1");
        main.setFlag();
    }
}

上面代码中有两个线程,主线程和创造出来的线程;被创造的线程等待变量变为true,在不为true的时候,一直在wait(),主线程把该变量变为true,并调用notify方法。

wait/notify方法只能在synchronized代码块内被调用;

虽然是在synchronized方法内,但调用wait方法的时候,线程会释放对象锁;

wait/notify方法被不同的线程调用,但共享相同的锁和条件等待队列,围绕一个共享的条件变量进行协作。

1.生产者/消费者模型

生产者往一个容器内放数据,如果满了就wait(),消费者从容器内取数据,如果空了就wait()。

 class MyBlockingQueue<E>{
    private Queue<E> queue = null;
    private int limit;
    public MyBlockingQueue(int limit){
        this.limit=limit;
        queue=new ArrayDeque<>(limit);
    }
    public synchronized void put(E e) throws InterruptedException {
        while (queue.size()==limit){
            wait();
        }
        queue.add(e);
        notifyAll();
    }
    public synchronized E take() throws InterruptedException {
        while (queue.isEmpty()){
            wait();
        }
        E e = queue.poll();
        notifyAll();
        return e;
    }
}

上面代码相当于一个仓库,limit相当于仓库的容量,仓库满了,生产者就wait,仓库空了,消费者就wait。

当仓库不为满的时候,生产者向仓库中放数据;

class Producer implements Runnable{
     MyBlockingQueue<String> queue;

    public Producer(MyBlockingQueue<String> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        int num = 0;
        try{
            while (true){
                String task = String.valueOf(num);
                queue.put(task);
                System.out.println("produce task"+task);
                num++;
                Thread.sleep((int)(Math.random()*100));
            }
        }catch (InterruptedException e){
        }
    }
}

上面为一个简单的生产者模型,循环向仓库里生产数据;

当仓库为空的时候,消费者不再向仓库取数据:

class Consumer implements Runnable{
    MyBlockingQueue<String> queue;

    public Consumer(MyBlockingQueue<String> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try{
            while (true){
                String task = queue.take();
                System.out.println("hadle task"+task);
                Thread.sleep((int)(Math.random()*100));
            }
        }catch (InterruptedException e){

        }
    }
}

上面为一个简单的消费者模型,循环向仓库取数据。

 public static void main(String[] args)  {
        MyBlockingQueue<String> queue = new MyBlockingQueue<>(10);
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }

启动该模型后,会发现生产者和消费者交替出现。

2.同时开始

相当于比赛场上,运动员听到裁判的枪声然后一起出发的过程;

class FireFlag{
    private volatile boolean fired = false;
    public synchronized void waitForFire() throws InterruptedException {
        while (!fired){
            wait();
        }
    }
    public synchronized void setFired(){
        this.fired=true;
        notifyAll();
    }
}

上述代码相当于一把信号枪的作用,每个线程在fired为false的时候,都必须等待,当枪响的时候所有线程就同时运行;

class Racer implements Runnable{
    FireFlag fireFlag;

    public Racer(FireFlag fireFlag) {
        this.fireFlag = fireFlag;
    }
    @Override
    public void run() {
        try {
            this.fireFlag.waitForFire();
            System.out.println("start run"+Thread.currentThread().getName());
        } catch (InterruptedException e) {

        }
    }
}

上面的代码是运动员的角色,每一个线程都要以“枪”来变换自身的状态;


    public static void main(String[] args) throws InterruptedException {
        int num = 10;
        FireFlag fireFlag = new FireFlag();
        Thread[]threads = new Thread[num];
        for(int i = 0;i<num;i++){
            threads[i] = new Thread(new Racer(fireFlag));
            threads[i].start();
        }
        Thread.sleep(1000);
        fireFlag.setFired();
    }

主线程相当于裁判,由它来确定枪响不响。

上面创建了十个线程,在枪未响的时候,都在赛道上等待,当主线程调用了setFired()后,也就是枪响的时候,所有线程被唤醒,执行接下来的操作。

3.等待结束

class MyLatch{
    private int count;

    public MyLatch(int count) {
        this.count = count;
    }
    public synchronized void await() throws InterruptedException {
        while(count>0){
            wait();
        }
    }
    public synchronized void countDown(){
        count--;
        if(count<=0){
            notifyAll();
        }
    }
}
class Worker implements Runnable{
    MyLatch latch;

    public Worker(MyLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep((int)(Math.random()*1000));
            this.latch.countDown();
        } catch (InterruptedException e) {

        }
    }
}
public class Main  {

    public static void main(String[] args) throws InterruptedException {
      int workerNum = 100;
      MyLatch latch = new MyLatch(workerNum);
      Thread[] threads = new Thread[workerNum];
      for(int i =0;i<workerNum;i++){
          threads[i]=new Thread(new Worker(latch));
          threads[i].start();
      }
      latch.await();
        System.out.println("collect  worker  results");
    }
}

上面就是创建了100个线程,每个线程都会sleep一会儿,当sleep结束之后,会进行count--,然后主线程会调用await方法,然后当count为0的时候,主线程就算可以知道所有的线程结束了,就可以接着往下继续运行。

是不是感觉跟join方法差不多,不过这个是不需要调用多次join方法来进行等待。

4.集合点

就相当于组队旅游的例子,大家先自己忙自己的事,之后再统一汇合。

class AssemblePoint{
    private int n;

    public AssemblePoint(int n) {
        this.n = n;
    }
    public synchronized void await() throws InterruptedException {
        if(n>0){
            n--;
            if(n==0){
                notifyAll();
            }else {
                while (n!=0){
                    wait();
                }
            }
        }
    }
}
public class Main  {
    static class Tourist implements Runnable{
        AssemblePoint ap;

        public Tourist(AssemblePoint ap) {
            this.ap = ap;
        }

        @Override
        public void run() {
            try {
                Thread.sleep((int)(Math.random()*1000));
                ap.await();
                System.out.println("arrivaed");
            } catch (InterruptedException e) {

            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int num =10;
        Thread [] to = new Thread[num];
        AssemblePoint ap = new AssemblePoint(num);
        for(int i = 0;i<num;i++){
            to[i]=new Thread(new Tourist(ap));
            to[i].start();
        }

    }
}

上述代码,创建了是个线程,然后每个线程都会执行自己的任务,执行完之后同意调用await方法到达集合点,进行等待,当所有线程都执行完了自己的任务,就会被唤醒。这个机制跟上述的等待结束机制有些相似,但却又不一样,上述等待机制是主线程等待由它创建的所有线程都结束为目的;集合点机制,就像是一个小分队里面自觉等待队友集合完毕,很相似,但是又很不一样。

以上就是我对线程间基本的协作机制的一点了解,其实线程还可以组合起来做很多事情,就如同一个游戏还有无限种方法等着我们去开发出来。

好了,不说了,敲代码了!!!

 

猜你喜欢

转载自blog.csdn.net/volatile_0524/article/details/82784971