文章目录
文章目录
前言
我要光明正大地学习,然后惊艳所有人~~~
本次任务的内容是记录的我的三个案例,这三个案例很重要,其基础模型思想涉及到后面的项目开发中的许多设计思想,一定一定要弄懂它
提示:以下是本篇文章正文内容,下面案例可供参考
一、车站卖票的案例:
适用场景:多线程中不同但具有相同功能的线程来操纵共有的数据, 有票1000张。有三个售票窗口,并通过这三个售票窗口进行销售,销售完毕后就显示票已经卖完提示、
创建线程任务类(卖票的任务)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Task implements Runnable{
private int ticket = 1000;//由于三个窗口都需要卖票,所以设为全局变量
private Lock lock = new ReentrantLock();
@Override
public void run() {
while(ticket > 0){
lock.lock();
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票");
ticket--;
}
if(ticket <= 0){
System.out.println(Thread.currentThread().getName() + "票已售完");
}
lock.unlock();
}
}
}
创建启动测试类
public class Test {
public static void main(String[] args) {
/**
* 2.铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,
* 请编写多线程程序来模拟这个效果
窗口001正在销售第1000张票
窗口001正在销售第999张票
窗口002正在销售第998张票
。。。
窗口002正在销售第1张票
问题1:三个窗口都卖了1000张票,一共卖了3000张
出现原因:创建了三个售票任务
解决方案:创建一个售票任务
问题2:有些票卖了重票
出现原因:票的输出语句输出后,还没有来得及做票的减减,就被其他线程抢到CPU资源了
解决方案:票的输出语句 和 票的减减必须同时执行完毕后,才能被其他线程抢到CPU资源了 - 加锁
问题3:出现负数
出现原因: 票到了零界点(ticket=1),三个线程都进入循环中
解决方案:锁中再判断一次
*/
Task task = new Task();
Thread t1 = new Thread(task, "窗口001");
Thread t2 = new Thread(task, "窗口002");
Thread t3 = new Thread(task, "窗口003");
t1.start();
t2.start();
t3.start();
}
}
二、“生产者”与“消费者”案例
适用场景:当不同功能的线程控制着同一个资源的时候使用,本次案例通过卖手机的方式来展示
案例一、一个“生产者”与“消费者”的情况下
创建一个手机实体类-----phone
public class Phone {
private String brand;
private double price;
public boolean isStore;
public Phone() {
}
public Phone(String brand, double price) {
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isStore(){
return isStore;
}
public void setStore(boolean isStore){
this.isStore=isStore;
}
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
创建生产者线程
public class Producer extends Thread{
private Phone phone;
public Producer(Phone phone){
this.phone=phone;
}
@Override
public void run() {
boolean flag=true;
while (true){
synchronized (phone){
if(phone.isStore()){//有库存
try {
phone.wait();//wait:使当前线程等待,并把等待的线程记录在对象监视器(phone)中
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(flag){
phone.setBrand("华为");
phone.setPrice(3999);
}else{
phone.setBrand("小米");
phone.setPrice(1999);
}
flag = !flag;
phone.setStore(true);//设置为有库存
phone.notify();//唤醒:唤醒对象监视器中第一个等待的线程
}
}
}
}
创建 消费者线程
public class Consumer extends Thread {
private Phone phone;
public Consumer(Phone phone) {
this.phone = phone;
}
@Override
public void run() {
while (true){
synchronized (phone){
if (!phone.isStore()){//没有库存
try {
phone.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(phone.getBrand()+"----"+phone.getPrice());
phone.setStore(false);//设置成没有库存
phone.notify();//唤醒:唤醒对象监视器中的第一个等待的线程
}
}
}
}
创建Test测试
public class Test01 {
public static void main(String[] args) {
/**
* 知识点:生产者消费者模型
*
* 注意:生产一个消费一个
*步骤:
* 1.让生产者线程和消费者线程 共同操作同一个手机对象(资源)
* 2.让两个产品来回切换(目的是放大第一个步骤的问题)
* 加锁,防止脏数据的出现
* 3.生产一个消费一个
*/
Phone phone=new Phone();
Producer p=new Producer(phone);
Consumer c=new Consumer(phone);
p.start();
c.start();
}
}
结果展示
案例二、“多生产者”与“多消费者”情况下
提示:案例二与案例一的区别在 生产者(Producer) 与消费者(Consumer)的线程方法中,理解使用while 与 if 的不同情况下的不同影响。
实体类 phone
public class Phone {
private String brand;
private double price;
private boolean isStore;
public Phone(){
}
public Phone(String brand,double price){
this.brand=brand;
this.price=price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isStore() {
return isStore;
}
public void setStore(boolean store) {
isStore = store;
}
}
创建生产者线程类
public class Producer extends Thread{
private Phone phone;
private static boolean flag=true;
public Producer(Phone phone) {
this.phone = phone;
}
@Override
public void run() {
while (true){
synchronized (phone){
while (phone.isStore()){//生产者有库存
try {
phone.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (flag){
phone.setBrand("华为");
phone.setPrice(3999);
}else {
phone.setBrand("小米");
phone.setPrice(1999);
}
flag=!flag;//使得 华为 与 小米 交替生产
phone.setStore(true);//设置为有库存即 生产者 将进入等待wait
phone.notifyAll();//唤醒对象监视器中所有等待的线程
}
}
}
}
消费者线程类
//消费者线程
public class Consumer extends Thread{
private Phone phone;
public Consumer(Phone phone) {
this.phone = phone;
}
@Override
public void run() {
while (true){
synchronized (phone){
while (!phone.isStore()){//没有库存
try {
phone.wait();//使当前线程处于等待并保留其进程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(phone.getBrand()+"----"+phone.getPrice());
phone.setStore(false);//设置成没有库存
phone.notifyAll();//唤醒对象监视器中所有等待的线程
}
}
}
}
测试类Test01
public class Test01 {
public static void main(String[] args) {
/**
* 知识点:多生产者 与 多消费者的模型
* 注意:多个生产者多个消费者的情况
* 需求:生产一个消费一个
* 步骤:1、让生产者和消费者线程共同操作同一个手机对象(资源)
* 2、让两个产品来回切换,(目的是放大第一个步骤的问题)
* 3、生产一个消费一个
*
*
*总结:
* 1、多个生产者多个消费者 if 变成 while ,初始唤醒线程之后再次判断是否符合库存没有就在一次等待
* 2、对象监视器该如何选择? 锁对象可以当做对象监视器使用,因为锁对象是多个线程共用的,二对象监视器可以在A线程中唤醒B线程
* 3、 sleep(休眠)属于Thread类的方法,不会释放锁,wait(等待)属于Object类的方法,会释放锁
*
*/
Phone phone=new Phone();
Producer p1 = new Producer(phone);
Producer p2 = new Producer(phone);
Consumer c1 = new Consumer(phone);
Consumer c2 = new Consumer(phone);
p1.start();
p2.start();
c1.start();
c2.start();
}
}
三、仓储模型
使用场景:通过一个中间角色来调控生产者与消费者两个线程之间的生产关系
附图理解
案例如下:
案例一、单个“生产者”与“消费者”情况下
创建蛋糕实体类 Cake
public class Cake {
private String brand;
private String dataTime;
public Cake(){
}
public Cake(String brand,String dataTime){
this.brand=brand;
this.dataTime=dataTime;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getDataTime() {
return dataTime;
}
public void setDataTime(String dataTime) {
this.dataTime = dataTime;
}
@Override
public String toString() {
return "Cake{" +
"brand='" + brand + '\'' +
", dataTime='" + dataTime + '\'' +
'}';
}
}
创建 仓库类 Store
import java.util.LinkedList;
public class Store {
//蛋糕容器
private LinkedList<Cake> list=new LinkedList<>();//由于涉及到先生产的蛋糕想卖掉的关系所以和队列有关,因此选择LinkedList来进行储存
//最大容量
private int maxCapacity=20;
//当前容量
private int currentCapacity;
//入库
public void push(Cake cake) {
synchronized (this){
if (currentCapacity>=maxCapacity){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(cake);
currentCapacity++;
System.out.println("入库成功,当前的容量为:"+currentCapacity);
this.notify();
}
}
//出库的方法
public void pop(){
synchronized (this){
if (currentCapacity<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Cake cake=list.removeFirst();
currentCapacity--;
System.out.println("出库成功,当前容量为:"+currentCapacity+"---"+cake);
this.notify();
}
}
}
创建生产者线程
import java.text.SimpleDateFormat;
import java.util.Date;
public class Producer extends Thread {
private Store store;
public Producer(Store store){
this.store=store;
}
@Override
public void run() {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
while (true){
Cake cake=new Cake("面包",sdf.format(new Date()));
store.push(cake);
}
}
}
创建消费者线程
public class Consumer extends Thread {
private Store store;
public Consumer(Store store) {
this.store = store;
}
@Override
public void run() {
while (true){
store.pop();
}
}
}
案例二、多个“生产者”与“消费者”情况
区别:还是与之前的模式一样 就是在 仓库Store中的时候对于生产者与消费者的不同操作 使用 while 或 if 来控制 最后达到唤醒 notifyAll() 唤醒多个任务线程的目的
案例如下:
创建实体类Cake
public class Cake {
private String brand;
private String dataTime;
public Cake(){
}
public Cake(String brand, String dataTime) {
this.brand = brand;
this.dataTime = dataTime;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getDataTime() {
return dataTime;
}
public void setDataTime(String dataTime) {
this.dataTime = dataTime;
}
@Override
public String toString() {
return "Cake{" +
"brand='" + brand + '\'' +
", dataTime='" + dataTime + '\'' +
'}';
}
}
创建仓库Store
import java.util.LinkedList;
public class Store {
//蛋糕容器
private LinkedList<Cake> list=new LinkedList<>();
//最大容量
private int maxCapacity;
//当前容量
private int currentCapacity;
//入库的方法
public void push(Cake cake){
synchronized (this){
while (currentCapacity>=maxCapacity){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
list.add(cake);//符合条件的话 就直接加入到仓库里去
currentCapacity++;
System.out.println("入库,当前容量为:"+currentCapacity);
notifyAll();//如果没有满仓库的话 就用这个唤醒所有线程 满了的话 这句话就没有啥用处
}
//出库的方法
public void pop(){
synchronized (this){
while (currentCapacity<0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Cake cake=list.removeFirst(); //如果满了 就移除掉第一个蛋糕,先做出来的先卖掉 否则会过起先
currentCapacity --;
System.out.println("出库成功,当前容量为:"+currentCapacity+"---"+cake);
this.notifyAll();
}
}
}
创建生产者线程
import java.text.SimpleDateFormat;
import java.util.Date;
public class Producer extends Thread{
private Store store;//由于需要操纵仓库 所以需要传递一个仓库
//构造方法
public Producer(Store store) {
this.store = store;
}
@Override
public void run() {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
while (true){
Cake cake=new Cake("桃李面包",sdf.format(new Date()));
store.push(cake);
}
}
}
创建消费者线程
public class Consumer extends Thread {
private Store store;
public Consumer(Store store) {
this.store = store;
}
@Override
public void run() {
while (true){
store.pop();
}
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了以后项目中经常要使用到的线程三大设计思想理念模式,我个人会死记于心。