生产者消费者的几种实现
1 lock锁实现
class SharaData{
private int num = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//生产
public void increment(){
lock.lock();
try {
//必须是while判断,否则可能出现虚假唤醒。在多个生产者消费者情况下会出现问题
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"生产");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement(){
lock.lock();
try {
while(num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"消费");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
SharaData sharaData = new SharaData();
new Thread(()->{
for (int i = 0; i < 10; i++) {
sharaData.increment();
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
sharaData.decrement();
}
}).start();
}
}
2 阻塞队列实现
class ShareData{
private volatile Boolean flag = true;
private BlockingQueue<String> blockingQueue = null;
private AtomicInteger num = new AtomicInteger(0);
public ShareData(BlockingQueue blockingQueue){
this.blockingQueue = blockingQueue;
}
//生产者
public void product() throws InterruptedException {
String data = null;
boolean offer;
while(flag){
data = "产品"+num;
num.incrementAndGet();
offer = blockingQueue.offer(data, 1, TimeUnit.SECONDS);
if(offer){
System.out.println("生产"+data);
}else {
System.out.println("生产"+data+"失败");
}
Thread.sleep(500);
}
}
//消费者
public void consume() throws InterruptedException {
String poll = null;
while(flag){
poll = blockingQueue.poll(2, TimeUnit.SECONDS);
if(null == poll || "".equals(poll)){
System.out.println("消费失败");
}else {
System.out.println("消费"+poll);
}
Thread.sleep(700);
}
}
//停止生产和消费
public void stop(){
flag = false;
}
}
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
ShareData shareData = new ShareData(queue);
new Thread(()->{
try {
shareData.product();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
shareData.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.stop();
System.out.println("停止生产和消费");
}
两个线程交叉打印问题。
线程A打印123456…线程B打印abcdefg…交叉打印结果1a2b3c…
-
利用synchronized同步锁加wait/notify实现
两个细节:1、如何保证一定是第一个线程先执行?使用CountDownLatch
2、一定是先notify再wait,在for循环之后必须notify,否则程序不会结束,一定有一个线程在等待唤醒
public static void main(String[] args) { char[] arr1 = "123456".toCharArray(); char[] arr2 = "abcdef".toCharArray(); Object o = new Object(); CountDownLatch countDownLatch = new CountDownLatch(1); new Thread(()->{ synchronized (o){ for(char c: arr1){ System.out.println(c); countDownLatch.countDown(); //执行完这个指令线程2才能执行 try { o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } o.notify(); //打印结束后一定要唤醒另一个线程 } }, "t1").start(); new Thread(()->{ try { countDownLatch.await(); //若这个线程先执行,则等待 } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o){ for(char c:arr2){ System.out.println(c); try { o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } o.notify();//打印结束后一定要唤醒另一个线程 } }).start(); }
-
利用LockSupport实现
public class Main { static Thread t1 ; static Thread t2 ; //LockSupport必须传入静态变量 public static void main(String[] args) { char[] arr1 = "123456".toCharArray(); char[] arr2 = "abcdef".toCharArray(); t1 = new Thread(()->{ for(char c:arr1){ System.out.println(c); //第一个线程先打印 LockSupport.unpark(t2); //打印完唤醒第二个线程 LockSupport.park(); //然后阻塞本线程 } }); t2 = new Thread(()->{ for(char c:arr2){ LockSupport.park(); //先阻塞本线程 System.out.println(c); //唤醒后执行打印 LockSupport.unpark(t1); //打印后唤醒线程1 } }); t1.start(); t2.start(); } }
-
利用ReentrantLock和condition实现。实现思路与synchronized一样,只不过使用condition精确唤醒
public static void main(String[] args) { char[] arr1 = "123456".toCharArray(); char[] arr2 = "abcdef".toCharArray(); ReentrantLock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); new Thread(()->{ try{ lock.lock(); //要想使用condition,先加锁 for(char c: arr1){ System.out.println(c); //先打印 condition2.signal(); //打印完成后唤醒另一个线程 condition1.await(); //将线程等待并释放锁 } condition2.signal(); //线程执行完毕后记得唤醒另一个线程 } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } }).start(); new Thread(()->{ try{ lock.lock(); for(char c: arr2){ System.out.println(c); condition1.signal(); condition2.await(); } condition1.signal(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } }).start(); }