多线程、等待唤醒机制

等待唤醒机制案例

//包子类
		public class BaoZi {
			//皮
			String pi;
			//陷
			String xian;
			//包子的状态: 有 true,没有 false,设置初始值为false没有包子
			boolean flag = false;
		}


		//生产者
		public class BaoZiPu extends Thread{
			//1.需要在成员位置创建一个包子变量
			private BaoZi bz;

			//2.使用带参数构造方法,为这个包子变量赋值
			public BaoZiPu(BaoZi bz) {
				this.bz = bz;
			}

			//设置线程任务(run):生产包子
			@Override
			public void run() {
				//定义一个变量
				int count = 0;
				//让包子铺一直生产包子
				while(true){
					//必须同时同步技术保证两个线程只能有一个在执行
					synchronized (bz){
						//对包子的状态进行判断
						if(bz.flag==true){
							//包子铺调用wait方法进入等待状态
							try {
								bz.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}

						//被唤醒之后执行,包子铺生产包子
						//增加一些趣味性:交替生产两种包子
						if(count%2==0){
							//生产 薄皮三鲜馅包子
							bz.pi = "薄皮";
							bz.xian = "三鲜馅";
						}else{
							//生产 冰皮 牛肉大葱陷
							bz.pi = "冰皮";
							bz.xian = "牛肉大葱陷";

						}
						count++;
						System.out.println("包子铺正在生产:"+bz.pi+bz.xian+"包子");
						//生产包子需要3秒钟
						try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						//包子铺生产好了包子
						//修改包子的状态为true有
						bz.flag = true;
						//唤醒吃货线程,让吃货线程吃包子
						bz.notify();
						System.out.println("包子铺已经生产好了:"+bz.pi+bz.xian+"包子,吃货可以开始吃了");
					}
				}
			}
		}


		//消费者
		public class ChiHuo extends Thread{
			//1.需要在成员位置创建一个包子变量
			private BaoZi bz;

			//2.使用带参数构造方法,为这个包子变量赋值
			public ChiHuo(BaoZi bz) {
				this.bz = bz;
			}
			//设置线程任务(run):吃包子
			@Override
			public void run() {
				//使用死循环,让吃货一直吃包子
				while (true){
					//必须同时同步技术保证两个线程只能有一个在执行
					synchronized (bz){
						//对包子的状态进行判断
						if(bz.flag==false){
							//吃货调用wait方法进入等待状态
							try {
								bz.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}

						//被唤醒之后执行的代码,吃包子
						System.out.println("吃货正在吃:"+bz.pi+bz.xian+"的包子");
						//吃货吃完包子
						//修改包子的状态为false没有
						bz.flag = false;
						//吃货唤醒包子铺线程,生产包子
						bz.notify();
						System.out.println("吃货已经把:"+bz.pi+bz.xian+"的包子吃完了,包子铺开始生产包子");
						System.out.println("----------------------------------------------------");
					}
				}
			}
		}


		//测试类
		public class Demo {
			public static void main(String[] args) {
				//创建包子对象;
				BaoZi bz =new BaoZi();
				//创建包子铺线程,开启,生产包子;
				new BaoZiPu(bz).start();
				//创建吃货线程,开启,吃包子;
				new ChiHuo(bz).start();
			}
		}
	案例二:有100个包,分别在官网和实体店卖出,使用等待唤醒机制
//Bag类
		public class Bag {
			private int num;

			public Bag(int num) {
				this.num = num;
			}

			public int getNum() {
				return num;
			}

			public void setNum(int num) {
				this.num = num;
			}
		}
		//实体店类实现Runnable接口
		public class ReallyStore implements Runnable{
			private Bag b;

			public ReallyStore(Bag b) {
				this.b = b;
			}

			@Override
			public void run() {
				while(true) {
					synchronized (b)
					{
						if(b.getNum() == 0)
						{
							b.notifyAll();
							break;
						}
						b.notify();

						int num = b.getNum();   //获取包的数量
						System.out.println(Thread.currentThread().getName() + "正在卖出第" + (100 - num + 1) + "个包包,还剩余" + ( --num));
						b.setNum(num);   //将变化后的宝的数量重新set
						try {
							b.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}

			}
			//网点类实现Runnable 其实代码和上面实体店类里面的代码是一样的
			public class Web implements Runnable{

				private Bag b;

				public Web(Bag b) {
					this.b = b;
				}

				@Override
				public void run() {
					while (true)
					{
						synchronized (b)
						{
							if(b.getNum() == 0)    //当包包的数量为0的时候就结束循环
							{
								b.notifyAll();
								break;
							}
							b.notify();

							int num = b.getNum();   //获取包的数量
							System.out.println(Thread.currentThread().getName() + "正在卖出第" + (100 - num + 1) + "个包包,还剩余" + ( --num));
							b.setNum(num);   //将变化后的宝的数量重新set
							try {
								b.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
				
				//测试类
				public class Test {
					public static void main(String[] args) {
						Bag b = new Bag(100);
						new Thread(new Web(b),"官网").start();
						new Thread(new Web(b),"时地点").start();
					}
				}

		
2.线程池
	线程池就是一个包含多个线程的一个容器
	当有任务需要用到线程的时候,从容器取出来进行使用。用完以后再把线程归还给池中
	//线程类
	public class RunnableImpl implements Runnable{
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
			}
		}


		//测试类
		public class Demo01ThreadPool {
			public static void main(String[] args) {
				//1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
				ExecutorService es = Executors.newFixedThreadPool(2);
				//3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
				es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
				//线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
				es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
				es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行

				//4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
				//es.shutdown();

				//es.submit(new RunnableImpl());//抛异常,线程池都没有了,就不能获取线程了
			}
		}
二、Lambda表达式
	1.定义格式
		(参数):代表要执行的接口中的某个方法,如果有参数就传递。如果没有小括号中空着
		->:将上面的小括号中的参数传递给方法内部
		{}:重写方法后要执行的代码

	2.Lambda表达式的练习-无参数无返回值
		
//接口
		public interface Cook {
			//定义无参数无返回值的方法makeFood
			public abstract void makeFood();
		}

		//测试类
		public class Demo01Cook {
			public static void main(String[] args) {
				//使用Lambda表达式,简化匿名内部类的书写
				invokeCook(()->{
					System.out.println("吃饭了");
				});
			}

			//定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFood
			public static void invokeCook(Cook cook){
				cook.makeFood();
			}
		}
3.Lambda表达式的练习-有参数有返回值
	public class Demo01Arrays {
			public static void main(String[] args) {
				//使用数组存储多个Person对象
				Person[] arr = {
						new Person("柳岩",38),
						new Person("迪丽热巴",18),
						new Person("古力娜扎",19)
				};

				//使用Lambda表达式,简化匿名内部类
				Arrays.sort(arr,(Person o1, Person o2)->{
					return o1.getAge()-o2.getAge();
				});

				//优化省略Lambda
				//Arrays.sort(arr,(o1, o2)->o1.getAge()-o2.getAge());

				//遍历数组
				for (Person p : arr) {
					System.out.println(p);
				}
			}
		}
4.Lambda表达式的练习-自定义接口
	//接口
		public interface Calculator {
			//定义一个计算两个int整数和的方法并返回结果
			public abstract int calc(int a,int b);
		}

		//测试类
		public class Demo01Calculator {
			public static void main(String[] args) {
				//使用Lambda表达式简化匿名内部类的书写
				invokeCalc(120,130,(int a,int b)->{
					return a + b;
				});

				//优化省略Lambda
				//invokeCalc(120,130,(a,b)-> a + b);
			}

			/*
				定义一个方法
				参数传递两个int类型的整数
				参数传递Calculator接口
				方法内部调用Calculator中的方法calc计算两个整数的和
			 */
			public static void invokeCalc(int a,int b,Calculator c){
				int sum = c.calc(a,b);
				System.out.println(sum);
			}
		}
5.使用Lambda表达式的前提和省略规则
	使用前提:
		A:必须是接口
		B:接口中只能有一个抽象方法
	省略格式:
		Lambda表达式:是可推导,可以省略
		凡是根据上下文推导出来的内容,都可以省略书写
		可以省略的内容:
			1.(参数列表):括号中参数列表的数据类型,可以省略不写
			2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
			3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
				注意:要省略{},return,分号必须一起省略
	

6.约束接口中只能有一个抽象方法的注解
	@FunctionalInterface   将一个接口规范为函数式接口

你可能感兴趣的:(java学习笔记,多线程,等待唤醒机制,Java学习笔记)