多线程之间的通信

什么是多线程之间的通信?

  • 多个线程对同一个临界区进行不同的操作
  • 常用方法介绍:
    1. wait()方法, 让当前线程从运行状态变成阻塞状态,并释放占有锁。
    2. notify()方法,让当前线程从阻塞状态变成可运行状态,可竞争锁。
    3. notifyAll()方法,让所有线程从阻塞状态变成可运行状态,可竞争锁。

生产者消费者问题

  • 生产者消费者是非常经典的线程同步模型,在编程中也有非常多的应用。生产者消费者问题描述如下:一个或者多个生产者往消息队列里面插入数据;单个消费者从消息队列里面取出数据处理;并且对消息队列的操作必须是互斥的。

问题的初步解决

1.定义一个临界区,或者叫缓存区。

class Resouce {
    int goodCount = 0 ;

    void put(){
        goodCount++ ;
    }

    void get(){
        goodCount-- ;
    }
}

2.编写生产者模型:

class Productor implements Runnable{
    Resouce res ; 
    public Productor(Resouce res) {
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true)
        product(res) ;
    }

    private void product(Resouce res) {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        res.put();
        System.out.println("生产者生产了一个商品"+",剩余:"+res.goodCount);
    }

}

3.编写消费者模型:

class Consumer implements Runnable{
    Resouce res ;
    public Consumer(Resouce res) {
        // TODO Auto-generated constructor stub
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true)
        consume(res) ;
    }
    private void consume(Resouce res) {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        res.get();
        System.out.println(Thread.currentThread().getName()+"消费了一个商品"+",剩余:"+res.goodCount);
    }

}

4.编写main方法测试

Resouce res = new Resouce() ;
        Thread productor = new Thread(new Productor(res)) ;
        Thread consumer1 = new Thread(new Consumer(res),"消费者1") ;
        Thread consumer2 = new Thread(new Consumer(res),"消费者2") ;

        productor.start();
        consumer1.start();
        consumer2.start();
  • 运行结果(节选):
消费者2消费了一个商品,剩余:-1
消费者1消费了一个商品,剩余:-1
消费者2消费了一个商品,剩余:-2
消费者1消费了一个商品,剩余:-2
生产者生产了一个商品,剩余:-1
消费者1消费了一个商品,剩余:-2
消费者2消费了一个商品,剩余:-3
消费者2消费了一个商品,剩余:-5

由运行结果可以看出结果是完全错乱的,这是因为线程是不安全的。

代码改进1

  • 为了解决线程安全问题使用synchronize控制进入临界区的线程,保证每次只有一个线程进入临界区。

1.改进的生产者

class Productor implements Runnable{
    Resouce res ; 
    public Productor(Resouce res) {
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true)
        product(res) ;
    }

    private synchronized void product(Resouce res) {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        res.put();
        System.out.println("生产者生产了一个商品"+",剩余:"+res.goodCount);
    }

}

2.改进的消费者

class Consumer implements Runnable{
    Resouce res ;
    public Consumer(Resouce res) {
        // TODO Auto-generated constructor stub
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true)
        consume(res) ;
    }
    private synchronized void consume(Resouce res) {
        // TODO Auto-generated method stub
        if(res.goodCount > 0){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            res.get();
            System.out.println(Thread.currentThread().getName()+"消费了一个商品"+",剩余:"+res.goodCount);
        }
    }

}
  • 运行结果(节选):
生产者生产了一个商品,剩余:1
消费者2消费了一个商品,剩余:0
消费者1消费了一个商品,剩余:0
生产者生产了一个商品,剩余:1
消费者2消费了一个商品,剩余:-1
消费者1消费了一个商品,剩余:-1
生产者生产了一个商品,剩余:0

由运行结果可以看出通过加锁并不能完全解决问题。

代码改进2

  • 使用线程之间通信的方法来进行同步,从而解决问题。
  • 假设生产者线程每100毫秒生产一个产品之后通知所有线程进入临界区notifyAll(),消费者线程每200毫秒在竞争得到线程后进入临界区,否则等待wait(),定义临界区最大容量为5,如果满则生产线程等待。
    注意一定要在休眠wait()之前进行唤醒notify()操作,否则会发生死锁
    1.改进后的临界区:
class Resouce {
    int goodCount = 0 ;
    final int MAXSIZE = 5 ;

    synchronized void put(){
        if(goodCount < MAXSIZE){
            goodCount++ ;
            System.out.println("生产者生产了一个商品"+",剩余:"+goodCount);
        }else{
            try {
                notify();
                wait() ;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    synchronized void get(){
        if(goodCount > 0 ){
            goodCount-- ;
            System.out.println(Thread.currentThread().getName()+"消费了一个商品"+",剩余:"+goodCount);
        }else{
            try {
                notify();
                wait() ;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

2.改进后的生产者

class Productor implements Runnable{
    Resouce res ; 
    public Productor(Resouce res) {
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            res.put();
        }

    }   
}

3.改进后的消费者

class Consumer implements Runnable{
    Resouce res ;
    public Consumer(Resouce res) {
        // TODO Auto-generated constructor stub
        this.res = res ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            res.get();
        }
    }
}
  • 运行结果(节选):
生产者生产了一个商品,剩余:1
消费者1消费了一个商品,剩余:0
生产者生产了一个商品,剩余:1
生产者生产了一个商品,剩余:2
消费者1消费了一个商品,剩余:1
生产者生产了一个商品,剩余:2
生产者生产了一个商品,剩余:3
消费者1消费了一个商品,剩余:2
生产者生产了一个商品,剩余:3
生产者生产了一个商品,剩余:4
消费者1消费了一个商品,剩余:3
生产者生产了一个商品,剩余:4
生产者生产了一个商品,剩余:5
消费者1消费了一个商品,剩余:4
生产者生产了一个商品,剩余:5
消费者1消费了一个商品,剩余:4
消费者2消费了一个商品,剩余:3

由结果可见,利用线程之间的通信方法即睡眠/唤醒机制,可以完美的解决生产者消费者的问题。

猜你喜欢

转载自blog.csdn.net/u013634252/article/details/80541743