多个线程并行去操作共享数据问题的完美解答

线程通信的应用:经典例题:生产者/消费者问题

生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

首先来用一种单一线程操作共享数据的方法(基于同步锁实现数据安全机制):

public class ProductTest {

    public static void main(String[] args) {

        Clerk clerk = new Clerk();

        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                clerk.addProduct();
            }
        }, "生产者").start();

        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                clerk.reduceProduct();
            }
        }, "消费者").start();
    }
}


class Clerk {

    private int productNum = 0;

    public synchronized void addProduct() {

        if (productNum < 20) {
            System.out.println(Thread.currentThread().getName() + ":" + ++productNum);
            //增加产品后立刻唤醒消费线程
            notify();
        } else {
            //产品为20个的时候就停止生产进入等待状态
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public synchronized void reduceProduct() {

        if (productNum > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + productNum--);
            //消费一个产品后立刻唤醒生产线程去生产
            notify();
        } else {
            //没有产品时就等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

总结分析:本案例中设计2个线程,一个生产线程和一个消费线程,使用的是同步锁机制保证了线程安全问题,但是同时也隐藏着另外一个问题,当多增加几个消费线程的时候,并不能做到并行去消费,所以同步锁机制虽然保证了安全,但是效率会很低,接下来我会用另外一种写法来实现多个消费线程并行操作数据并且能够保证数据安全。

多个消费线程并行操作数据代码如下所示:

import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ProductTest {

    public static void main(String[] args) {
        Producer p = new Producer();
        Thread p1 = new Thread(p);
        p1.start();

        Consume c = new Consume();
        Thread c1 = new Thread(c);
        Thread c2 = new Thread(c);
        Thread c3 = new Thread(c);
        Thread c4 = new Thread(c);
        Thread c5 = new Thread(c);

        c1.setName("消费线程1");
        c2.setName("消费线程2");
        c3.setName("消费线程3");
        c4.setName("消费线程4");
        c5.setName("消费线程5");

        c1.start();
        c2.start();
        c3.start();
        c4.start();
        c5.start();

    }
}

class Contain {
    private static int count=1;
    private static int[] datas = new int[20];
    private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private static Lock readLock = readWriteLock.readLock();
    private static Lock writeLock = readWriteLock.writeLock();
    private static ThreadLocal<Integer> sizeThreadL = new ThreadLocal<Integer>();
    private static AtomicReference<Integer> sizeReference = new AtomicReference<Integer>(0);

    public void produceProduct() throws InterruptedException {
        writeLock.lock();
        sizeThreadL.set(sizeReference.get());
        if (sizeReference.get() < datas.length) {
            if (sizeReference.compareAndSet(sizeThreadL.get(), sizeReference.get() + 1)) {
                datas[sizeThreadL.get()] = count;
                System.out.println("生产数据:           " + count + "  ---时间为:" + new Date());
                count++;

            }
        }
        writeLock.unlock();
    }

    public void consumeProduct() throws InterruptedException {
        readLock.lock();
        sizeThreadL.set(sizeReference.get());
        if (sizeReference.get() > 0) {
            if (sizeReference.compareAndSet(sizeThreadL.get(), sizeReference.get() - 1)) {
                int data = datas[sizeThreadL.get() - 1];
                //这里是安全的
                System.out.println(Thread.currentThread().getName() + ": 消费数据: " + data + "  ---时间为:"+new Date());
                count--;
            }
        }
        readLock.unlock();
    }
}

class Producer extends Contain implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                produceProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consume extends Contain implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
                consumeProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如下图所示:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值