Java 多线程之等待唤醒机制和了解线程池-4

在这里插入图片描述

等待唤醒机制

线程间通信

  • 多个线程处理同一个资源,但是处理的动作(线程的任务)却不同
  • 就是上一篇文章的例子 Java 多线程之线程状态-3里面列举的生产者跟消费者的Demo

!!!为什么要处理线程间通信!!!

  • 多个线程并发执行时,默认情况CPU随机切换线程的,当我们需要多个线程来完成同一个任务
  • 我们希望他们有规律执行,那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据

!!!如何保证线程间通信有效利用资源!!!

  • 多个线程再处理同一资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一变量的使用或操作
  • 就是多个线程再操作同一数据时,避免对同一共享变量的争夺,也就是我们需要通过一定的手段使各个线程能有效利用资源,而这种手段叫做等待唤醒机制

等待唤醒机制

多个线程之间一种协作机制,谈到线程我们经常小岛的时线程间的竞争(race),比如争夺锁,但这并不是故事的全部,线程间也会有协作机制
就好比公司里你和你的同时,可能存再晋升的竞争,但更多的时候你们更多的是一起合作完成任务
就是一个线程进行了规定操作后,就进入等待状态wait(),等待其他线程执行完他们的指定代码过后,再将其唤醒notify()
再有多个线程进行等待时,如果需要,可以使用notifyAll()来唤醒所有等待线程

wait/notify 就是线程间的一种协作机制

等待唤醒中的方法

等待唤醒机制就是用于解决线程间通信的问题的,使用3个方法的含义如下

  • wait:线程不在活动,不在参与调度,进入 wait set中,因此不会浪费CPU资源,也不会竞争锁,这是线程状态时WAITING,它还要等着别的线程执行一个特别的动作,也就是通知( notify ) 再这个对象上等待的线程wait set 中释放出来,重新进入调度队列( ready queue)中
  • notify: 则选取所通知的对象wait set 中的一个线程释放;例如,参观有空位置后,等候就餐最久的顾客最先入座
  • notifyAll:释放所通知对象的wait set上全部的线程

注意: 哪怕只通知了一个等待线程,被通知的线程也不能立刻恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不在持有锁,所以它需要再次尝试获取锁(可能面临其他线程的竞争),成功后才能再当初调用wait方法之后恢复执行

总结如下:

  • 如果获取锁,线程就从WAITING状态变成RUNNABLE阶段
  • 否则,从wait set出来,又进入entry set

调用wait和notify方法要注意的细节?

  • wait方法与notify方法必须要由同一锁对象调用,因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
  • wait方法与notify方法时属于Object类中的方法,因为:锁对象可以时任意对象,而任意对象的所属类都是继承了Object类的
  • wait方法和notify方法必须要再同步代码块或时同步函数中使用,因为:必须要通过锁对象调用2个方法

生产者和消费者的问题

  • 等待唤醒机制其实就是经典生产者和消费者的问题
  • 就拿生产包子消费包子来说等待唤醒机制如何有效利用资源
    在这里插入图片描述
    !!!上代码!!!
// 包子类
public class BaoZi{
    // 皮
	String pi;
	// 馅
	String xian;
	boolean isHave = false;
}
// 包子店类
public class BaoZiPu implements Runnable{
	private BaoZi baoZi;
	
	public BaoZiPu(BaoZi baoZi){
		this.baoZi = baoZi;
	}

	@Override
	public void run(){
		int count = 0;
	
		while(true){
			synchronized(baoZi){
				// 如果存在包子 包子铺线程等待
				if(baoZi.isHave == true){
					baoZi.wait();
				}
				if (count % 2==0){
					baoZi.pi = "薄皮";
					baoZi.xian = "三鲜馅";
				}else{
					baoZi.pi = "冰皮";
					baoZi.xian = "牛肉大葱馅";
				}
				System.out.println("包子铺正在生产:"+baoZi.pi+baoZi.xian+"的包子");
				count++;
				// 三秒制作
				Thread.sleep(3000);
				System.out.println("包子铺生产好了:"+baoZi.pi+baoZi.xian+"的包子");

				// 设置包子状态
				baoZi.isHave = true;
				// 唤醒吃货线程
				baoZi.notify();
			}
		}
	}
}
// 包子店类
public class ChiHuo implements Runnable{
	private BaoZi baoZi;
	
	public ChiHuo(BaoZi baoZi){
		this.baoZi = baoZi;
	}

	@Override
	public void run(){
		while(true){
			synchronized(baoZi){
				// 如果不存在包子 吃货线程等待
				if(baoZi.isHave == false){
					baoZi.wait();
				}
				System.out.println("吃货正在吃:"+baoZi.pi+baoZi.xian+"的包子");
				System.out.println("吃货已经把:"+baoZi.pi+baoZi.xian+"的包子吃完了,包子铺开始生产包子");
				
				// 设置包子状态
				baoZi.isHave = false;
				// 唤醒包子铺线程
				baoZi.notify();
			}
		}
	}
}
public class EatBaoZiTest{
	public static void main(String[] args){
		
		BaoZi baoZi = new BaoZi();

		new BaoZiPu(baoZi).start();
		new ChiHuo(baoZi).start();
	}
}

!!!结果!!!

在这里插入图片描述

因为是无线循环所以,我们的预期是一样的!!!,这就是一个简单的生产者和消费者的案列

线程池概念

线程池思想概述

我们使用线程的时候就去创建一个线程,实现起来非常简单,但是会有一个问题:
如果并发线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统效率,因为频繁创建线程和销毁线程是需要时间
有没有一种办法使得线程可以复用,执行完一个任务,不被销毁,可以继续执行其他任务呢?

线程池概念

容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程的操作,无需反复创建小城而消耗过多的资源

在这里插入图片描述
合理利用线程池的三个好处

  • 降低资源消耗,减少了创建和销毁线程的次数,每个工作栈都可以被重复利用,可执行多个任务
  • 提高响应速度,当任务到达时,任务可以不需要等到线程就立刻执行
  • 提高线程可管理性,可以根据系统的承受能力,调整线程池工作栈线程的数目,防止消耗过多的内存,而导致服务器宕机(每个线程需要大约1MB内存,线程开的越多,消耗的内存越大)

线程池的使用

Java里面线程池的顶级接口 java.util.concurrent.Executor 严格意义上Executor并不是线程池,而只是一个执行线程的工具
真正的线程池接口java.util.concurrent.ExecutorService

注意:配置一个线程池比较复杂,尤其对于线程池原理不是很清楚的情况下,很有可能配置的线程池不是较优,因此Executors线程工厂类提供了一些静态工厂,生成一些常用的线程池, 官方建议用Executors工具类创建线程池对象

  • public static ExectorSercvice newFixedThreadPool(int nThreads); 返回线程池对象(指定线程池数目)

获取到了ExecutorService对象如何使用呢?

  • public Future<?> submit(Runnable task); 获取线程池中的某一线程的对象,并执行

Future接口:用来记录线程任务执行完毕产生的结果,线程池创建与使用

使用线程池线程对象的步骤:

  • 创建线程池对象
  • 创建Runnable 接口子类对象
  • 提交Runnable 接口子类对象
  • 关闭线程池(一般不做)

public class EatBaoZiTest{
	public static void main(String[] args){
		
		BaoZi baoZi = new BaoZi();
		ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.submit(new BaoZiPu(baoZi));
        executorService.submit(new ChiHuo(baoZi));
	}
}
发布了24 篇原创文章 · 获赞 33 · 访问量 2391

猜你喜欢

转载自blog.csdn.net/weixin_41241629/article/details/104279975