线程之间的通信

线程之间的通信

使用线程来运行多个任务时,可以通过使用锁来同步俩个任务的行为,从而使得一个任务不会干扰到另外一个任务,这是解决线程间彼此干涉的问题,现在我们需要来解决线程间彼此协调的问题问题,也就是线程之间通信的问题。–摘自百度

其实我在工作的期间一直对线程这个概念并不是很清晰,只要不是单独线程独立运行的话,就都会涉及线程之间的交互,那么就属于线程之间通信

  • join:一个线程让另外一个线程参见进来执行完成
  • 生产者/消费者:是生产者和消费者之间的协调

一 wait 和notify/notifyAll

在进行本小节的深入探讨的之前,我想抛出一个问题:什么是线程通信
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或者操作。换句简单的话来描述就是说:多个线程在操作同一份数据时,避免对同一个变量进行操作从而造成变量资源的争夺。
那么在面对这个问题的时候,我们就会想到,要是其中一个线程能睡着然后等休息好了,在对任务进行操作,这样就不会争抢而且还会有一种井然有序的感觉。
这样的话我们就引出唤醒机制:(wait(),notify())
就是在一个线程进行规定操作后,那么我就进入等待状态(wait),然后等其他线程将指定的任务完成之后,在将线程唤醒(notify)。

wait()方法

在其他线程调用对象时的notify方法或者 notifyAll方法前,那么我当前的线程就会等待。
线程调用wait()方法得时候,就会释放他对锁的拥有权,然后等待另外的线程来通知它,当然,唤醒线程的方式不可能是_‘喂!你该起来上班啦!!!’_他是有固定方法的。比如notifynotifyAll方法,使用这俩个方法才可以获得锁的拥有权。
~注意
要确保调用wait()方法的时候拥有锁那么就得确保wait()方法的调用必须放在synchronized方法或synchronized块中。

notify()方法

notify()方法会唤醒一个等待当前对象锁的线程

notifyAll()方法

notifyAll()方法会唤醒此对象监视器上等待的所有线程

~注意
如果多个线程在等待,它们中的一个将会选择被唤醒。
这种选择是随意的,和具体实现有关。
notify()方法应该是被拥有对象的锁的线程所调用。
notifyAll()方法同理

二 线程实例

其实不难理解线程之间最好的实例就是:生产者消费者模型,那么我们就来仿写一个生产者消费者的例子;

生产牛奶的方法以及消费的方法

package ThreadDemo;

/**
 * Created by jdx on 2021/12/29 下午3:56
 */
public class Milk {
    //生产牛奶的奶牛编号
    private int cowsId;
    //牛奶
    private int bottles;

    //生产牛奶的方法
    public synchronized void MilkTheCow() {
        //当牛奶生产的牛奶瓶子数不为0时,那生产牛奶的方法就停止
        if (bottles != 0) {
            try {
                //等待线程
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //那么当我的牛奶瓶数为0的时候奶牛就开始产奶
        bottles = bottles + 1;
        //为了区别是哪一个牛产的哪一瓶奶那么奶牛编号也得区分一下
        cowsId = cowsId + 1;
        //获取生产奶的奶牛
        String cowName = Thread.currentThread().getName();
        //从控制台输出
        System.out.println(cowName + " 生产了一瓶编号为" + cowsId + " 牛奶");
        //从这时候开始,就得唤醒其他等待的线程
        notify();
    }

    //消耗牛奶的方法
    public synchronized void drinkMilk() {
        //当牛奶瓶数为0的时候,这个方法处于等待状态
        if (bottles == 0) {
            try {
                wait();
                //InterruptedException 是 wait sleep 等方法自己抛出的 '中断异常'
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //消费牛奶
        bottles = bottles - 1;
        //人作为消费者
        String people = Thread.currentThread().getName();
        //从控制台输出
        System.out.println(people + "喝了一个牛奶,编号为:" + cowsId);
        //执行完成之后 去唤醒其他线程
        notify();
    }
    //get set 方法


    public int getCowsId() {
        return cowsId;
    }

    public void setCowsId(int cowsId) {
        this.cowsId = cowsId;
    }

    public int getBottles() {
        return bottles;
    }

    public void setBottles(int bottles) {
        this.bottles = bottles;
    }
    //无参构造

    public Milk() {
        super();
    }

    //有参构造

    public Milk(int cowsId, int bottles) {
        super();
        this.cowsId = cowsId;
        this.bottles = bottles;
    }
}

生产牛奶的类

package ThreadDemo;

/**
 * Created by jdx on 2021/12/30 下午6:01
 */
public class cows extends Thread {
    //获得牛奶的类
    private Milk milk;

    //无参构造
    public cows() {
        super();
    }

    //有参构造
    public cows(Milk milk) {
        super();
        this.milk = milk;
    }

    //get set 方法
    public Milk getMilk() {
        return milk;
    }

    public void setMilk(Milk milk) {
        this.milk = milk;
    }
    //重写run方法

    @Override
    public void run() {
        productMilk();
    }

    private void productMilk() {
        //默认生产25瓶牛奶
        for (int i = 0; i < 2; i++) {
            //调用生产牛奶的方法
            milk.MilkTheCow();
        }
    }
}

消费牛奶的类

package ThreadDemo;

/**
 * Created by jdx on 2021/12/30 下午6:12
 */
public class people extends Thread {
    //获得牛奶的类
    private Milk milk;

    //get set 方法

    public Milk getMilk() {
        return milk;
    }

    public void setMilk(Milk milk) {
        this.milk = milk;
    }
    //重写run方法

    @Override
    public void run() {
        drink();
    }

    private void drink() {
        for (int i = 0; i < 2; i++) {
            //调用消费牛奶的方法
            milk.drinkMilk();
        }
    }


    //有参构造
    public people(Milk milk) {
        super();
        this.milk = milk;
    }

    //无参构造
    public people() {
        super();
    }

}

测试单元

package basics;

import ThreadDemo.Milk;
import ThreadDemo.cows;
import ThreadDemo.people;

/**
 * Created by jdx on 2021/12/30 下午6:17
 */
public class milkTest {
    public static void main(String[] args) {
        //new 一个 Milk 类
        Milk milk = new Milk();
        //new 一个 cows 类
        cows cow = new cows(milk);
        //new 一个 people 类
        people humanity = new people(milk);
        //new 一个 包含生产者的线程
        Thread threadCow = new Thread(cow, "生产者:奶牛");
        //new 一个 包含消费者的线程
        Thread threadHum = new Thread(cow, "消费者:人类");
        //启动线程
        threadCow.start();
        threadHum.start();
    }
}

多多感悟

上一篇:Parity game


下一篇:paxos算法之边角