消费者和生产者模式
一.概念
1.生产者消费者不是设计模式,属于线程之间的经典问题,生产者生产什么消费者消费什么,生产者不生产消费者不能消费
实现代码:
先创建一个产品类
package com.qfedu.test1;
/**
* 产品类
* @author WHD
*
*/
public class Computer {
private String mainFrame;
private String screen;
private boolean proState;// 默认值为false表示 可以生产不能消费 值为true表示可以 消费 不能生产
public boolean isProState() {
return proState;
}
public void setProState(boolean proState) {
this.proState = proState;
}
public String getMainFrame() {
return mainFrame;
}
public void setMainFrame(String mainFrame) {
this.mainFrame = mainFrame;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public Computer() {
}
public Computer(String mainFrame, String screen) {
this.mainFrame = mainFrame;
this.screen = screen;
}
@Override
public String toString() {
return "Computer [mainFrame=" + mainFrame + ", screen=" + screen + "]";
}
}
再创建生产者类,代码如下:
package com.qfedu.test1;
/**
* 生产者
* @author WHD
*
*/
public class Productor extends Thread{
private Computer com;
public Productor(Computer com) {
this.com = com;
}
@Override
public void run() {
// run方法用于生产电脑
// 根据for循环i的取值分别生产两种电脑 一共20台
// i为偶数生产 联想电脑
// 为奇数生产 华硕电脑
for (int i = 1; i <= 20; i++) {
synchronized (com) {
if(com.isProState()) {
try {
com.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
com.setMainFrame("联想主机" + i);
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
com.setScreen("联想显示器" + i);
} else {
com.setMainFrame("=======华硕主机=======" + i);
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
com.setScreen("=======华硕显示器=======" + i);
}
com.setProState(true);
com.notify();
}
}
}
}
代码解释:
先定义一个产品,然后写上有参构造,再来个for循环,我们需要生产多少次就让i<=多少,然后用synchronized去锁定我们要传入的产品,使其只能每次只能生产一次,然后判断我们定义的ProState是否为flase,是的话表示现在有产品,需要等待消费者购买以后在进行生产,否则没有产品我们将进行生产,并在生产完成以后把状态修改为true,并随机调用一个其他等待的方法,因本案例中只有一个消费者,故调用消费者
消费者代码:
package com.qfedu.test1;
/**
* 消费者
* @author WHD
*
*/
public class Consumer extends Thread{
private Computer com ;
public Consumer(Computer com) {
this.com = com;
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
synchronized (com) {
if(!com.isProState()) {
try {
com.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("消费者获取电脑:" + com.getMainFrame() + ">>>>>" + com.getScreen());
com.setProState(false);
com.notify();
}
}
}
}
代码解释:
也是先创建产品类,然后for循环进行购买,判断其状态是否为true,为true则可进行购买,否则则等待生产者生产完,并在购买玩将其状态修改为folase,并调用其它等待的方法。
队列
一.概念:队列可用于解决消费者和生产者一次只能生产一个的情况,队列可自定义生产数量,不必等消费者购买以后进行生产,队列如果满了就不再进行生产,如果队列中没有东西,消费者无法购买,也是多个线程同用一个队列
生产代码:
package com.qfedu.test2;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 之前我们使用Object提供一些方法可以实现生产者消费者模式
* 但是这种方式有很明显的缺点:
* 生产者和消费者严重"捆绑"在一起
* 生产者生产一个 消费者才能消费一个
* 我们现实生活 中肯定不是这样的
* 所以我们可以使用队列来解决这个问题
* 队列 所有的队列都要这样一个特点 FIFO (first in first out )
* Java中的队列由JDK给我们提供的一个接口 Queue<T>
*
*
* 1.先定义生产者和消费者 还是两个线程
* 2.两个线程要使用同一个队列 同一个队列对象
* ArrayBlockingQueue 是基于数组的 通过查看源代码 当前类对象会根据我们传入的长度作为队列的大小 数组的长度
* @author WHD
*
*/
public class Test {
public static void main(String[] args) {
// 1.队列如果满了就不再往队列中存放
// 2.消费者如果发现队列中没有内容 不会消费
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);
Productor pro = new Productor(queue);
// Consumer con = new Consumer(queue);
pro.start();
// con.start();
}
}
代码解释:
先定义一个队列类,并定义其一次可以生产的数量,然后定义有参构造,重写run方法,for循环我们需要生产的次数,然后用队列对象调用put方法进行生产,不用等待和重新调用,生产满了以后将自动进行等待
消费者代码:
package com.qfedu.test2;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 消费者
* @author WHD
*
*/
public class Consumer extends Thread{
private ArrayBlockingQueue<String> queue;
public Consumer(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
try {
System.out.println("消费者获取到的数据:" + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
代码解释:
先定义队列对象,然后for循环,直接在for里进行输出我们获取到的生产者生产的产品,用队列对象.take获取
锁
一.种类:自旋锁(无锁) CAS 轻量级锁(偏向锁) 重量级锁(排它锁) 读写锁 读锁,写锁
可重入锁:同一个线程重复获取一个锁对象 是指synchorized代码块小括号中再有synchorized
package com.qfedu.test3;
/**
* 锁 synchronized
* 自旋锁(无锁) CAS 轻量级锁 (偏向锁) 重量级锁(排它锁) 读写锁 读锁 写锁
* 锁的粒度 粗 细
* 比如有一段代码 程序 在这段代码中有对数据读写的操作
* 我们发现 读数据的操作我们通常不需要上锁
* 写数据的操作都要上锁
* Lock
* 可重入锁:同一个线程重复获取同一个锁对象 是指的 synchronized代码块小括号中的内容 不应该产生 死锁
* synchronized属于可重入锁
* @author WHD
*
*/
public class Test1{
// public void m1() {
// synchronized (this) {
// count --;
// }
// }
public static void main(String[] args) {
Object o = new Object();
synchronized (o) {
System.out.println("外层同步代码快");
synchronized (o) {
System.out.println("内层同步代码块");
}
}
}
}
自定义可重入锁
package com.qfedu.test3;
/**
* 不可重入锁
* 可重入锁:同一个线程重复获取同一个锁对象 (是指的 synchronized代码块小括号中的内容 ) 不应该产生 死锁
* @author WHD
*
*/
public class TestLock {
public MyLock lock = new MyLock();
public static void main(String[] args) throws InterruptedException {
TestLock test = new TestLock();
test.m1();
// test.m2();
}
public void m1() throws InterruptedException {
lock.lock();
System.out.println("m1方法执行");
m2();
lock.unlock();
}
public void m2() throws InterruptedException {
lock.lock();
System.out.println("m2方法执行");
lock.unlock();
}
}
class MyLock{
// 锁 具备两个功能 一个上锁 一个解锁
// 我们如何判断当前是该上锁 还是该 解锁
private boolean lockState; // 默认值为false 没有上锁 上锁以后改为true
private Thread thread; // 用于和调用上锁和解锁方法的线程比较 是否为 同一个线程 同一个线程如果重复调用上锁的方法 就不上锁
private int holdCount; // 上锁的次数 每次上锁就+1 每次解锁就 -1 当值为0的时候 释放锁
public synchronized void lock() throws InterruptedException {
Thread t = Thread.currentThread();
while(lockState && t != thread) {
wait();
}
lockState = true;
thread = t;
holdCount ++;
}
public synchronized void unlock() {
Thread t = Thread.currentThread();
if(t == thread) {
holdCount --;
if(holdCount == 0) {
lockState = false;
notify();
}
}
}
}
代码解释:
先定义一个锁类,定义三个私有参数,一个判断是否上锁,一个判断是否是同一个对象,一个判断上锁次数,定义一个synchronized修饰的上锁方法,然后判断获取当前线程信息赋值给t,然后判断当前是否上锁并且是否是同一线程,当没有上锁,且为同一线程则进行上锁,并修改lockState状态,类记上锁次数。
定义synchronized修饰的开锁方法,然后获取当前线程信息,如果线程为同一线程,则上锁次数减一,然后上锁次数不为0则继续等待下一次开锁操作,将上锁次数继续减一直到为0进行开锁操作,并调用等待的线程。