并发编程04--线程通讯

多线程通讯

线程之间的通信机制有两种:共享内存和消息传递。

在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信。

共享内存

多线程之间以共享内存方式通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。第一个线程写入(input)用户,另一个线程取读取(out)用户.实现读一个,写一个操作。通过synchronized锁确定线程安全。

代码如下:

class ShareData{
    private int number = 0;
    public synchronized void sendEmail() throws InterruptedException {
        System.out.println(" send email");     
    }
    public synchronized void sendSms() throws InterruptedException {        
        System.out.println(" send sms");
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
     ShareData sd = new ShareData();

     new Thread(()->{
        sd.sendEmail();
     },"A").start();

     new Thread(()->{
        sd.sendSms();
     },"B").start();
    }
}

消息传递

消息传递的核心就是多个线程轮流操作资源类,过程涉及两个核心概念:

  1. 同一个资源类;
  2. 状态判断机制即依据资源状态获取资源操作权利;                      

wait、notify方法

典型的线程操作同变量的场景是生产者和消费者,为保证线程安全可以使用wait、notify方式,使用需要遵守下列原则:

  1. 因为涉及到对象锁,他们必须都放在synchronized中来使用. Wait、Notify一定要在synchronized里面进行使用。
  2. Wait必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行
  3. notify/notifyall: 唤醒因锁池中的线程,使之运行

注意:一定要在线程同步中使用,并且是同一个锁的资源

代码

class ShareData{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        //判断
        if (number!=0){
            this.wait();
        }
        ++number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        //唤醒
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
     ShareData sd = new ShareData();

     new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"A").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

waitsleep区别

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

虚假唤醒

在上述案例中,如果存在多个生产这同时生产数据,代码如下

public class ThreadDemo2 {
    public static void main(String[] args) {
     ShareData sd = new ShareData();

     new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"A").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
    new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"C").start();
    new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"D").start();
}

运行结果:

生产和消费没有交替出现,原因在于资源状态判断时使用的是if,会造成连续两个生产线程进入wait等待区,在消费数据后,连续生产数据。如下图所示:

解决方式:将IF判断改成while判断,即每次线程获取到资源操作权限后,重新判断资源状态

class ShareData{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        //判断
        while (number!=0){    //将if该换成while
            this.wait();
        }
        ++number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        //唤醒
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }
}

定制化通信

1 创建多个Condition;

2 每个Condition代表一个线程;

3 设置标志位,依据标志位确定哪个Condition被激活;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值