面试挺容易让你当场写个生产者消费者的代码,于是总结了下,以便往后复习用。
代码包含了wait/notify和lock所实现的,不仅有一对一还有多对多模式
1.一生产和一消费:操作值-实现
生产者消费者其实就是基于wait/notify原理所实现的,话不多说,先上例子
生产者类:
package com.zz.p_r_test;
public class P {
private String lock;
public P(String lock) {
super();
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
if (!ValueObject.value.equals("")) {
//第二次执行进入wait并释放锁
lock.wait();
}
String value = System.currentTimeMillis() + "_"
//返回最准确的可用系统计时器的当前值,以毫微秒为单位
+ System.nanoTime();
System.out.println("set的值是" + value);
ValueObject.value = value;
//notify执行完成后,锁是不自动释放的
lock.notify();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
消费者类:
package com.zz.p_r_test;
public class C {
private String lock;
public C(String lock) {
super();
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
if (ValueObject.value.equals("")) {
//第一次false
lock.wait();
}
System.out.println("get的值是" + ValueObject.value);
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
生产者线程类:
package com.zz.p_r_test;
public class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
super();
this.p = p;
}
@Override
public void run() {
while(true) {
p.setValue();
}
}
}
消费者线程类:
package com.zz.p_r_test;
public class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
super();
this.r = r;
}
@Override
public void run() {
while(true) {
r.getValue();
}
}
}
工具类:
package com.zz.p_r_test;
public class ValueObject {
public static String value = "";
}
Run类:
package com.zz.p_r_test;
public class Run {
public static void main(String[] args) throws InterruptedException {
String lock = new String();
P p = new P(lock);
C r = new C(lock);
ThreadP pThread = new ThreadP(p);
pThread.start();
ThreadC cThread = new ThreadC(r);
cThread.start();
}
}
运行结果:
get的值是1554031923112_2195489417655118
set的值是1554031923112_2195489417696173
get的值是1554031923112_2195489417696173
set的值是1554031923112_2195489417732952
get的值是1554031923112_2195489417732952
set的值是1554031923112_2195489417770585
get的值是1554031923112_2195489417770585
set的值是1554031923112_2195489417808219
get的值是1554031923112_2195489417808219
......
打印日志可以看出,get和set是交替运行的。
但是如果在此实验的基础上实现多生产,多消费,那么程序极有可能出现假死现象,也就是所有的线程都进入waiting状态
2.多生产与多消费:操作值-假死
生产者消费者模式很容易出现假死现象,所以再次举例子:
生产者类:
public class P {
private String lock;
public P(String lock) {
super();
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while (!ValueObject.value.equals("")) {
//第二次执行进入wait并释放锁
System.out.println("生产者"
+ Thread.currentThread().getName() + "WAITING了♣");
lock.wait();
}
System.out.println("生产者"
+ Thread.currentThread().getName() + "RUNNABLE了");
String value = System.currentTimeMillis() + "_"
//返回最准确的可用系统计时器的当前值,以毫微秒为单位
+ System.nanoTime();
ValueObject.value = value;
//notify执行完成后,锁是不自动释放的
lock.notify();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
消费者类:
public class C {
private String lock;
public C(String lock) {
super();
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("消费者"
+ Thread.currentThread().getName() + "WAITING了?");
//第一次false
lock.wait();
}
System.out.println("消费者"
+ Thread.currentThread().getName() + "RUNNABLE了");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
生产者线程类:
public class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
super();
this.p = p;
}
@Override
public void run() {
while(true) {
p.setValue();
}
}
}
消费者线程类:
public class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
super();
this.r = r;
}
@Override
public void run() {
while(true) {
r.getValue();
}
}
}
依旧是那个工具类:
public class ValueObject {
public static String value = "";
}
最后一个当然是跑一跑的类啦:
public class Run {
public static void main(String[] args) throws InterruptedException {
String lock = new String();
P p = new P(lock);
C r = new C(lock);
ThreadP[] pThread = new ThreadP[2];
ThreadC[] cThread = new ThreadC[2];
for(int i = 0; i<2; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName(" 生产者 " + (i + 1));
cThread[i] = new ThreadC(r);
cThread[i].setName(" 消费者 " + (i + 1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
//getThreadGroup返回该线程所属线程组 ,activeCount() 返回当前线程的线程组中活动线程的数目
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
//enumerate(Thread[] tarray) 将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for(int i = 0; i < threadArray.length; i++) {
//getState 返回线程状态
System.out.println(threadArray[i].getName() + " "
+ threadArray[i].getState());
}
}
}
运行结果:
消费者 消费者 1WAITING了?
消费者 消费者 2WAITING了?
生产者 生产者 2RUNNABLE了
生产者 生产者 2WAITING了♣
生产者 生产者 1WAITING了♣
消费者 消费者 1RUNNABLE了
消费者 消费者 1WAITING了?
消费者 消费者 2WAITING了?
main RUNNABLE
生产者 1 WAITING
消费者 1 WAITING
生产者 2 WAITING
消费者 2 WAITING
从运行结果可以得出,notify通知的等待线程有可能是消费者的,也有可能是生产者的。就比如”生产者“唤醒”生产者“,”消费者“唤醒”消费者“这样的情况。如果按这样的情况运行的比率积少成多,就会导致所有的线程都不能运行下去,大家都在等待,最后肯定就进入了WAITING状态。
所以只需要讲notify改成notifyAll即可,这样唤醒所有线程,就不至于进入假死状态了。
3.Lock实现一对一生产者消费者模式
package com.thread.yiduiyi;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
condition.await();
}
System.out.println("生产");
hasValue = true;
condition.signal();// 相当于Ojbect类中的wait
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("消费者");
hasValue = false;
condition.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
线程A
package com.thread.yiduiyi;
public class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
线程B
package com.thread.yiduiyi;
public class ThreadB extends Thread{
private MyService myService;
public ThreadB(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
跑跑类:
package com.thread.yiduiyi;
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
threadA.start();
ThreadB threadB = new ThreadB(myService);
threadB.start();
}
}
4.Lock实现多对多生产者消费者模式
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
System.out.println("有可能连续生产⭐");
condition.await();
}
System.out.println("生产");
hasValue = true;
condition.signalAll();// 相当于Ojbect类中的wait
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
System.out.println("有可能连续消费**");
condition.await();
}
System.out.println("消费者");
hasValue = false;
condition.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
线程A
package com.thread.duoduiduo;
public class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
线程B
package com.thread.duoduiduo;
public class ThreadB extends Thread{
private MyService myService;
public ThreadB(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
跑跑类:
package com.thread.duoduiduo;
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA[] threadA = new ThreadA[10];
ThreadB[] threadB = new ThreadB[10];
for(int i = 0; i < 10; i++) {
threadA[i] = new ThreadA(myService);
threadB[i] = new ThreadB(myService);
threadA[i].start();
threadB[i].start();
}
}
}
输出结果就不放出了,基本上跟同步块代码的输出结果相似