java第18天----生产者消费者,Lock锁,守护线程,join()方法

昨天知识总结:

  • 多线程
    • 线程的两种创建方式
      • 创建Thread的子类
      • 线程与任务分离
    • 线程安全----重点
    • 单例中线程安全的应用
    • 线程的停止

线程

线程的通信

在这里插入图片描述

  • 线程的通信:

  • 分析:

  • 需要两个线程—输入线程,和输出线程,

  • 需要两个任务===输入任务和输出任务

  • 需要一份数据

  • 使用唤醒等待机制实现:wait()/notify()/notifyAll()等

  • 实现:打印机打印–不断地输入不断地输出

  • 分析:需要输入任务和输出任务共用一把锁,保证两个任务之间是同步的

  • 给两个任务加一把锁,可以是this或Object.class

  • 不建议使用Object.class原因:由于Object.class的范围太大了,会造成不必要的错误

  • 使用this最合适,因为他只被两个线程共享

  • 注意:只给一个线程加锁,无法实现两个线程的同步;

	public static void main(String[] args) {
		//1.创建数据类对象
		Des des = new Des();
		//2.创建任务类对象
		Input input = new Input(des);
		Output output = new Output(des);
		//3.创建线程
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.开启线程
		inputThread.start();
		outputThread.start();
	}
}
//创建输入任务
class Input implements Runnable{
	Des des;
	
	public Input() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Input(Des des) {
		super();
		this.des = des;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		int i = 0;
		while(true) {
			synchronized (des) {
				if(i == 1) {
					des.name = "凤姐";
					des.sex = "男";
				}else {
					des.name = "芙蓉姐姐";
					des.sex = "女";
				}
				i = (i+1)%2;
			}
		}
	}
}
//创建输出任务
class Output implements Runnable{
	Des des;
	
	public Output(Des des) {
		super();
		this.des = des;
	}

	public Output() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			synchronized (des) {
				System.out.println(des);
			}
		}
	}
}
//创建数据类
class Des{
	String name;
	String sex;
	@Override
	public String toString() {
		return "Des [名字=" + name + ", 性别=" + sex + "]";
	}
}
  • wait():让当前的线程进入等待状态,他会被放入一个池子(程序池),失去了抢CPU的能力,等待唤醒,(锁—相当于给当前的线程做了一个标记)
  • notify():让当前的线程从等待线程唤醒,相当于从池子中取出线程(唤醒的是同一把锁下的任意一个线程)
  • notifyAll():唤醒的是同一把锁下所有线程。
	public static void main(String[] args) {
		//1.创建数据类对象
		Des1 des = new Des1();
		//2.创建任务类对象
		Input1 input = new Input1(des);
		Output1 output = new Output1(des);
		//3.创建线程
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.开启线程
		inputThread.start();
		outputThread.start();
	}
}
//创建输入任务
class Input1 implements Runnable{
	Des1 des;
	
	public Input1() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Input1(Des1 des) {
		super();
		this.des = des;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub

		int i = 0;
		while(true) {
			if(i == 0) {
				des.setData("凤姐", "男");
			}else {
				des.setData("芙蓉姐姐", "女");
			}
			i = (i+1)%2;
		}
	}
}
//创建输出任务
class Output1 implements Runnable{
	Des1 des;
	
	public Output1(Des1 des) {
		super();
		this.des = des;
	}

	public Output1() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub

		while(true) {
			des.get();
		}

	}
}
//创建数据类
//封装类的原则:是你的活你做,不是你的活不要做
class Des1{
	String name;
	String sex;
	Boolean flag = false;//创建一个标识,控制唤醒与等待的切换
	//获取数据--处理输入
	public synchronized void setData(String name,String sex) {
		 
		if(flag == true) {
			try {//让输入线程等待,当flag的值为true的时候
				//在执行代码的时候,这里对应的是那个线程,锁对象 操纵的就是哪个线程
				wait();//只要一执行wait,线程就会立即停止在这里,等待下次唤醒
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		this.sex = sex;
//			if(i == 1) {
//				des.name = "凤姐";
//				des.sex = "男";
//			}else {
//				des.name = "芙蓉姐姐";
//				des.sex = "女";
//			}
//			i = (i+1)%2;
		flag = !flag;
			
		notify();//唤醒输出线程
				//当执行唤醒的时候,在线程池中没有找到被当前的所标记的线程,我们称为空唤醒,空唤醒对程序没有影响,程序允许空唤醒		
	}
	//将数据打印到控制台-处理数据
	public synchronized void get() {
			if(flag == false) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("Des1 [名字=" + name + ", 性别=" + sex + "]");
			flag = !flag;
			notify();
		
	}
	@Override
	public String toString() {
		return "Des1 [名字=" + name + ", 性别=" + sex + "]";
	}
}

生产者消费者

  • 多线程的设计模式:生产着消费者
  • 两类:
  • 1.单生产者单消费者
  • 2.多生产者多消费者
单生产者单消费者
  • 分析:生产线程,消费线程
  • 生产任务,消费任务
  • 产品
	public static void main(String[] args) {
		//1.创建产品
		Product product= new Product();
		//2.创建生产任务,消费任务
		Producer producer = new  Producer(product);
		Consumer consumer = new Consumer(product);
		//创建生产线程,消费线程
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);
		//4.开启线程
		t1.start();
		t2.start();
	}
}
//创建产品类
class Product{
	String name;
	double price;
	int count;
	
	boolean flag = false;//用来唤醒与等待之间切换
	
	//为生产者准备数据
	public synchronized void setProduce(String name,double price) {
		if(flag == true) {
			try {
				wait();//让生产线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.name = name;
			this.price = price;
			System.out.println(Thread.currentThread().getName()+ "  生产了:"+name+"  产品的数量:"+count+"  产品的价格:"+price);
		}
		count++;
		flag = !flag;
		notify();//唤醒消费线程

	}
	//为消费者准备数据
	public synchronized void consume() {
		if(flag == true) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}		
			System.out.println(Thread.currentThread().getName()+ "  生产了"+name+"  产品的数量:"+count+"  产品的价格"+price);
		}
		flag = !flag;
		notify();//唤醒生产线程
	}
}
//创建生产任务
class Producer implements Runnable {
	Product product;
	
	public Producer() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Producer(Product product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.setProduce("bingbing", 10);
		}
	}
}
//创建消费任务
class Consumer implements Runnable {
	Product product;
	
	public Consumer() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Consumer(Product product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.consume();
		}
	}
}
多生产者多消费者

在这里插入图片描述

  • 分析:生产线程,消费线程有多个
  • 生产任务,消费任务各有一个
  • 产品一个
	public static void main(String[] args) {
		//1.创建产品
		Product1 product = new Product1();
		//创建生产任务,消费任务
		Producer1 producer1 = new Producer1(product);
		Consumer1 consumer1 = new Consumer1(product);
		//3.创建生产线程,消费线程
		Thread t0 = new Thread(producer1);
		Thread t1 = new Thread(producer1);
		Thread t2 = new Thread(consumer1);
		Thread t3 = new Thread(consumer1);
		//开启线程
		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
//创建产品类
class Product1{
	String name;
	double price;
	int count;
	
	boolean flag = false;//用来唤醒与等待之间切换
	
	//为生产者准备数据
	public synchronized void setProduce(String name,double price) {
		while(flag == true) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		this.price = price;
		count += 1; 
		System.out.println(Thread.currentThread().getName()+ "  生产了"+name+"  产品的数量:"+count+"  产品的价格"+price);
		
		flag = !flag;
		notifyAll();
	}
	//为消费者准备数据
	public synchronized void consume() {
		while(flag == false) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
		System.out.println(Thread.currentThread().getName()+ "  消费了"+name+"  产品的数量:"+count+"  产品的价格"+price);
		flag = !flag;
		notifyAll();

	}
}
//创建生产任务
class Producer1 implements Runnable {
	Product1 product;
	
	public Producer1() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Producer1(Product1 product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.setProduce("bingbing", 10);
		}
	}
}
class Consumer1 implements Runnable {
	Product1 product;
	
	public Consumer1() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Consumer1(Product1 product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.consume();
		}
	}
}

Lock锁

  • 比较synchronized和Lock
  • 1.synchronized:从jdk1.0开始使用—隐式同步
  • synchronized(锁对象){//获取锁 我们简称这里的锁成为锁旗舰或者监听器
  •  同步代码块
    
  • }//释放锁
  • 2.Lock:从jdk1.5开始使用—显示同步
  • 原理:Lock是接口,我们要通过他的子类工作
  • 首先调用lock的lock()方法,获取锁
  •  进行同步操作的代码
    
  • 调用lock的unlock()方法,释放锁
  • 使用场景总结:当使用多生产者多消费者的时候,使用lock,其他的使用synchronized
  • 使用效率上lock比synchronized高。
    在这里插入图片描述
public static void main(String[] args) {
		//1.创建产品
		Product1 product = new Product1();
		//创建生产任务,消费任务
		Producer1 producer1 = new Producer1(product);
		Consumer1 consumer1 = new Consumer1(product);
		//3.创建生产线程,消费线程
		Thread t0 = new Thread(producer1);
		Thread t1 = new Thread(producer1);
		Thread t2 = new Thread(consumer1);
		Thread t3 = new Thread(consumer1);
		//开启线程
		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
//创建产品类
class Product2{
	String name;
	double price;
	int count;
	//创建Lock的对象
	Lock lock = new ReentrantLock();
	boolean flag = false;//用来唤醒与等待之间切换
	//创建用于生产者线程的Condition对象
	Condition conditionProducer = lock.newCondition();
	//创建用于消费者线程的Condition对象
	Condition conditionConsumer = lock.newCondition();
	
	//为生产者准备数据
	public  void setProduce(String name,double price) {
		try {
			lock.lock();
			while(flag == true) {
				try {
					conditionProducer.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			this.name = name;
			this.price = price;
			count += 1; 
			System.out.println(Thread.currentThread().getName()+ "  生产了"+name+"  产品的数量:"+count+"  产品的价格"+price);
			
			flag = !flag;
			conditionConsumer.signal();
		} finally {//必须执行的代码
			// TODO: handle finally clause
			lock.unlock();
		}

	}
	//为消费者准备数据
	public void consume() {
		try {
			lock.lock();
			while(flag == false) {
				try {
					conditionConsumer.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}		
			System.out.println(Thread.currentThread().getName()+ "  消费了"+name+"  产品的数量:"+count+"  产品的价格"+price);
			flag = !flag;
			conditionProducer.signal();
		} finally {
			// TODO: handle finally clause
			lock.unlock();
		}
	}
}
//创建生产任务
class Producer2 implements Runnable {
	Product2 product;
	
	public Producer2() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Producer2(Product2 product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.setProduce("bingbing", 80);
		}
	}
}
//创建消费任务
class Consumer2 implements Runnable {
	Product2 product;
	
	public Consumer2() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Consumer2(Product2 product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.consume();
		}
	}
}

守护线程

  • 守护线程:相当于后台线程,依赖于前台线程,正常情况下,当前台线程结束的时候,不管守护线程有没有结束,都会结束,
  • 典型的守护线程:垃圾回收线程
  • setDaemon(true)开启守护线程
	public static void main(String[] args) {
		Test test = new Test();
		Thread thread = new Thread(test);
		/*
		 * 当程序调用setDaemon的时候,并且将参数设置成true,他就变成了守护线程
		 * 注意:这个方法一定要在start方法之前调用
		 */
		thread.setDaemon(true);
		thread.start();
		
		int i = 0;
		while (true) {
			if(i++ == 10) {
				System.out.println(Thread.currentThread().getName()+"i:"+i);
				break;//主线程结束
			}
		}
	}
}
class Test implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			System.out.println("守护线程");
			
		}
	}
	
}

join()方法

  • join()方法:
  • 原理:程序一旦调用了join方法,它的优先级会高于主线程,意思是说,主线程会等当前线程执行完后再去执行,
  • 注意点:这个线程的优先级只比main高,对其他的线程没有影响
	public static void main(String[] args) {
		Dog dog = new Dog();
		Thread thread0 = new Thread(dog);
		Thread thread1 = new Thread(dog);
		thread1.start();
		thread0.start();
		/*
		 * 当线程开始工作后,让thread0调用join()方法,他的优先级会高于main线程
		 * 注意:join方法必须在线程开始后调用
		 */
		try {
			thread0.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for(int i = 0; i < 10;i++) {
			System.out.println(Thread.currentThread().getName()+"  i:"+i);
		}
	}
}
class Dog implements Runnable{
	public void run() {
		for(int i = 0; i < 10;i++) {
			System.out.println(Thread.currentThread().getName()+"  i:"+i);
		}
	}
}

线程状态图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者-消费者问题是一个经典的多线程并发问题,主要涉及到生产者线程和消费者线程之间的协作与同步。在这个问题,生产者线程负责生产数据,并将数据存入一个共享的缓冲区,而消费者线程则从缓冲区取出数据进行消费。为了避免生产者和消费者线程之间的竞争条件和死问题,需要使用线程同步技术。 下面是一个使用 Python 语言实现的生产者-消费者问题的示例代码: ```python import threading import time import random # 缓冲区大小 BUFFER_SIZE = 5 # 共享的缓冲区 buffer = [] # 生产者线程 class ProducerThread(threading.Thread): def run(self): global buffer while True: # 生产一个随机数 item = random.randint(1, 10) print("生产者生产了数据:", item) # 获取 lock.acquire() # 如果缓冲区已满,等待消费者线程消费数据 while len(buffer) >= BUFFER_SIZE: print("缓冲区已满,生产者等待...") lock.wait() # 将数据存入缓冲区 buffer.append(item) print("生产者将数据存入缓冲区:", buffer) # 释放 lock.release() # 随机等待一段时间 time.sleep(random.randint(1, 3)) # 消费者线程 class ConsumerThread(threading.Thread): def run(self): global buffer while True: # 获取 lock.acquire() # 如果缓冲区为空,等待生产者线程生产数据 while len(buffer) == 0: print("缓冲区为空,消费者等待...") lock.wait() # 从缓冲区取出数据进行消费 item = buffer.pop(0) print("消费者消费了数据:", item) # 释放 lock.release() # 随机等待一段时间 time.sleep(random.randint(1, 3)) # 创建 lock = threading.Condition() # 创建生产者线程和消费者线程 producer_thread = ProducerThread() consumer_thread = ConsumerThread() # 启动线程 producer_thread.start() consumer_thread.start() # 等待线程结束 producer_thread.join() consumer_thread.join() ``` 在这个示例代码,我们使用了 Python 的 Condition 类来实现线程同步和协作。在生产者线程,如果缓冲区已满,则使用 wait() 方法等待消费者线程消费数据;在消费者线程,如果缓冲区为空,则使用 wait() 方法等待生产者线程生产数据。当生产者线程向缓冲区添加数据或消费者线程从缓冲区取出数据时,需要使用 acquire() 方法获取,以避免竞争条件的发生。 需要注意的是,在生产者-消费者问题,线程同步和协作是非常重要的,如果实现不当,将会导致死、竞争条件等问题。因此,在实际开发,需要仔细设计和测试多线程程序,以确保程序的正确性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值