java线程基础

等待与唤醒

什么是等待唤醒机制
它是一种多线程的协作机制,就是在一个线程进行了规定操作后,就进入等待状态wait()
等待其他其他线程执行完他们指定的代码后,再将其唤醒(notify()) 在有多个线程进行等待时候,
如果需要,可以使用notifyAll()来唤醒所有的线程等待
wait/notify就是线程间一种协作机制

等待唤醒的方法

  1. wait:线程不在活动,不在参与调度,进入wait set中,不会浪费cpu资源,也不会去竞争锁了,这个时候线程
    的状态是waiting 它还要等待别的线程执行一个特别的动作 也就是通知notify 在这个对象上等待的线程从
    waitset释放出来,重新进入调度队列(ready queue)中

2.notif:选取所通知对象的wait set中的一个线程释放

3 notifyAll: 释放所通知对象的waitset上的全部线程

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

总结:
如果能获取锁,线程就从waiting状态变成runnable状态
否则,从wait set出来,又进入entry set,线程就从waiting状态又变成block状态

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

等待与唤醒机制其实就是:线程之间的通信
经典的生产者与消费者模式:
有效的资源(生产一个包子,吃一个包子,再生产一个,再吃)
通信:对包子的状态进行判断:
包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子
(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包
子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取
决于锁的获取情况

代码演示:
分析 :
需要哪些类
资源类: 包子类:
包子的属性:皮 馅儿
包子的状态:有为true,无:false
生产者(包子铺)类是一个线程类,可以继承thread
设置线程任务(run):生产包子
对包子的状态进行判断
true 有包子
包子铺调用wait方法进入状态
false: 没有包子
包子铺生产包子,生产好包子 修改包子的状态为true
唤醒吃货线程,让吃货线程吃包子
消费者: 吃货线程 继承线程thread
吃完后修改包子状态 且唤醒包子铺线程 生产包子

测试类:
main方法
创建包子对象
开启包子铺线程
吃货线程
代码:

public class Baozi{
  //皮
  String pi;
  //馅儿
  String xian;

  //报纸的状态
  BOOlean flag=false;
}

包子铺线程和包子铺线程关系 通信(互斥)
必须同时同步技术保证唯一,可以使用包子对象作为锁对象
包子铺类和吃货的类需要把包子对象作为参数传递进来
需要在成员位置创建一个包子变量
使用带参数的构造方法 为包子变量赋值

public clas BaoziPu extends Thread{
	//定义包子成员变量
	private Baozi bz;
	//构造方法
	public Baozi (Baozi  bz )
	{
		this.bz=bz;
	}
	// 设置线程任务:生产包子
	@Overrider
	public void run(){
		int count=0;
		//包子铺一直生产包子
		while(ture){
			synchronized(bz){
			if(bz.flag==true){
				//包子铺调用wait方法
			try{
			bz.wait()
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			}
			//被唤醒之后执行包子铺生产包子
			//增加趣味性:交替生产包子
			if(count%2==0){
				//生产 薄皮三鲜馅儿包子
				bz.pi="薄皮";
				bz.xian="三鲜馅";
			}else{
				//另一种包子
				
		}
		count++;
		打印(包子在生产);
		//生产包子需要3 秒,有异常try catch
		try{
			Thread.sleep(3000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		//包子铺生产好了
		// 修改包子状态 唤醒吃货状态
		bz.notify();
		
	}
	}
	
}

}

吃货线程

public class ChiHuo extends Thread{
		private BaoZi bz;
		public ChiHuo(String name,BaoZi bz){
		super(name);
		this.bz = bz;
	}
	@Override
	public void run() {
		while(true){
				synchronized (bz){
				if(bz.flag == false){//没包子
					try {
						bz.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
				bz.flag = false;
				bz.notify();
			}
		}
	}
}
 测试类 
	public class Demo {
	public static void main(String[] args) {
		//等待唤醒案例
		BaoZi bz = new BaoZi();
		ChiHuo ch = new ChiHuo("吃货",bz);
		BaoZiPu bzp = new BaoZiPu("包子铺",bz);
		ch.start();
		bzp.start();
	}
}


线程池

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低
系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在Java中可以通过线程池来达到这样的效果。

线程池原理:就是一个容器->集合(ArrayList Hashset,LinkedList,HashMap)
LinkedList 用这个最好

合理利用线程池能够带来三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内
    存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

线程池:
Jdk1.5之后提供的
java.util.concurent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
参数:
int nthreads:创建线程池中包含的数量
返回值:
ExecutorService接口,返回的是ExecotorService接口的实现类对象,我们可以使用ExecutorService接口接受(面向接口编程)

java.util.concurent.ExecutorService:线程池接口
	用来从线程池中获取线程,调用start方法,执行线程任务
		submit(Runnable task) 提交一个 Runnable 任务用于执行
		关闭/销毁线程池方法 shutdown()
	
线程池的使用步骤:
	参考代码:
	1.线程池类
		public class ThreadPoolDemo {
			public static void main(String[] args) {
				// 创建线程池对象
				ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
				// 创建Runnable实例对象
				MyRunnable r = new MyRunnable();
				//自己创建线程对象的方式
				// Thread t = new Thread(r);
				// t.start(); ‐‐‐> 调用MyRunnable中的run()
				// 从线程池中获取线程对象,然后调用MyRunnable中的run()
				service.submit(r);
				// 再获取个线程对象,调用MyRunnable中的run()
				service.submit(r);
				service.submit(r);
				// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
				// 将使用完的线程又归还到了线程池中
				// 关闭线程池
				//service.shutdown();
			}
		}


	2.Runnable实现类
		public class MyRunnable implements Runnable {
			@Override
			public void run() {
				System.out.println("我要一个教练");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
					System.out.println("教练来了: " + Thread.currentThread().getName());
					System.out.println("教我游泳,交完后,教练回到了游泳池");
			}
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值