等待和唤醒机制

线程间的通信,多个线程处理同一资源
	多线程在并发执行时,CPU是随机切换的,我们需要让他有规律的执行
	多个线程的协调通信,以此来达到共同操作统一资源,避免对同一变量的争夺。
	生产者与消费者之间的关系
多个线程之间的协作机制:
资源类,属性
================
/*
 * 	包子类:
 * 		资源类
 * 		设置包子属性
 * 		皮
 * 		馅
 * 		包子的状态: true 有 |  false 没有
 * 
 * 
 */
public class BaoZi {
	
	//皮
	String pi;
	//馅
	String xian;
	//包子的状态
	boolean flag = false;	
}

------------------------------
/*
 * 	包子铺类:
 * 		true 有包子
 * 		false 没有
 * 
 * 	必须使用同步技术保证只有一个在执行,不能抢夺资源
 * 	使用包子对象作为锁对象
 * 	包子铺类和吃货类需要把包子对象作为参数传进来
 * 		在成员位置定义一个包子变量
 * 		使用带参构造方法,为这个包子变量赋值	
 * 
 */
public class BaoZiPu extends Thread{

	//定义一个包子
	//这是一个线程类可以继承自Thread
	private BaoZi bz;

	public BaoZiPu() {
		super();
	}

	public BaoZiPu(BaoZi bz) {
		super();
		this.bz = bz;
	}
	//设置线程任务,生产包子
	@Override
	public void run() {
		int count = 0;
		while(true){
			synchronized (bz) {
				//对包子状态进行判断
				if(bz.flag=true){
					try {
						bz.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						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) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//生产好之后,包子状态为有
				bz.flag=true;
				//唤醒吃货线程
				bz.notify();
				System.out.println("包子已经做好");
			}
		}
		
	}
	
}

--------------------------------
/*
 * 	消费者类:
 * 		吃货类
 * 
 */
public class ChiHuo extends Thread{

	private BaoZi bz;
	public ChiHuo(BaoZi bz){
		this.bz=bz;
	}
	@Override
	public void run() {
		//死循环,让吃货一直吃包子
		while(true){
			//必须同时同步技术保证两个线程只能有一个在执行
			synchronized (bz) {
				if(bz.flag==false){
					//吃货进入等待
					try {
						bz.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				//线程被唤醒之后执行的代码
				System.out.println("吃货正在吃:"+bz.pi+bz.xian);
				//吃货吃完包子
				bz.flag = false;
				//唤醒线程生产包子
				bz.notify();
				System.out.println("吃货还要吃,老板继续做");
				System.out.println("----------------------------------");
			}
		}
	}
	
}

----------------------------------
public class WaitAndNotify {
	//测试类
	public static void main(String[] args) {
		//创建对象
		BaoZi bz = new BaoZi();
		new BaoZiPu(bz).start();
		new ChiHuo(bz).start();
	}
}

=======================

/*
 * 	定义接口
 * 
 */
public interface Calculator {

	//计算两个int整数和的方法
	public abstract int calc(int a, int b);
}

----------------------
/*
 * Lambda表达式可推导可省略
 * 		凡是根据上下文书写出来的内容都是可以省略书写
 * 		可以省略的内容:
 * 			参数列表:括号中参数列表的类型,可以省略不写
 * 				       括号中参数如果只有一个,那么类型和()都可以省略
 * 				       如果大括号中的代码只有一行,无论是否有返回值,都可以省略({} return ;) 他们三个必须一起省略
 * 		可以推导出来的才能省略
 * 		使用Lambda必须具有接口
 * 		使用Lambda必须具有上下文推断
 * 		有且只有一个抽象方法的接口称之为函数式接口。
 */
public class DemoArrayList {

	public static void main(String[] args) {
		
	}
}

-----------------------
public class DemoCalcutor {

	public static void main(String[] args) {
		//调用
		invokeCalc(10, 20, new Calculator() {
			
			@Override
			public int calc(int a, int b) {
				// TODO Auto-generated method stub
				return a+b;
			}
		});
		//使用Lambds
		invokeCalc(122, 130, (int a,int b)->{
			return a+b;
		});
		
		//优化
		invokeCalc(122, 130, ( a, b)-> a+b);
	}
	//定义一个方法,参数传递两个int类型的整数,参数传递接口,方法内部调用接口中的方法
	public static void invokeCalc(int a, int b,Calculator c){
		int sum = c.calc(a, b);
		System.out.println(sum);
	}
}

==================

/*
 *	 练习1:
 * 		定义一个厨子Cook接口,内含抽象方法makeFood
 * 		
 */
public interface Cook {
	//定义一个无参无返回值的方法
	public abstract void makeFood();
}

----------------------------------
/*
 * Lambda表达式:
 * 		标准格式:	一些参数  一个箭头  一段代码
 * 				(参数) -> { 重写方法的代码 }
 * 			():接口中抽象方法的参数列表,没有就空着,有就写,多个参数使用逗号隔开
 * 			->:传递
 * 			{}:重写接口的抽象方法的方法体
 * 
 * 
 */
public class DemoLambda {

	public static void main(String[] args) {
		//使用匿名内部类
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"创建新的线程");
				
			}
		}).start();;
		
		//使用Lambda表达式,把括号里面的参数传到大括号里面的输出语句
		new Thread(()-> {
			
			System.out.println(Thread.currentThread().getName()+"创建新的线程");
		 }
		).start();
		//优化省略Lanbda
		new Thread(()-> System.out.println(Thread.currentThread().getName()+"创建新的线程")).start();
		
	}
	
}

-----------------------------
/*
 * 	使用实现Runable接口方式实现多线程
 * 		这个类有很多冗余的代码
 * 
 */
public class DemoRunnable {

	public static void main(String[] args) {
		//创建
		RunnableImpl run = new RunnableImpl();
		//创建Thread类对象
		Thread t = new Thread(run);
		t.start();
		
		
		//简化代码,使用匿名内部类,使用接口接收一下
		Runnable r = new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"创建新的线程");
				
			}
		};
		new Thread(r).start();
		
		
		//继续简化代码,使用匿名内部类
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"创建新的线程");
				
			}
		}).start();;
				
	}
}

----------------------------------
public class LambdaTest01 {

	public static void main(String[] args) {
		//调用,参数是Cook接口
		invokeCook(new Cook(){
			public void makeFood(){
				System.out.println("开饭");
			}
		});
		
		//使用Lambda表达式,简化书写
		invokeCook(()->{
			System.out.println("开饭");
		});
		invokeCook(()->System.out.println("开饭"));
		
	}
	//定义一个方法,参数传递Cook接口,方法的内部调用Cook接口中方法makeFood
	public static void invokeCook(Cook cook){
		cook.makeFood();
	}
}

--------------------------------
import java.util.Arrays;
import java.util.Comparator;

public class LambdaTestArray {

	public static void main(String[] args) {
		//创建数组,数组中的元素使用逗号隔开
		Person[] arr = {
				new Person("古力娜扎", 18),
				new Person("迪丽热巴", 20),
				new Person("小爱", 22)
		};
		//对数组中的进行升序排列
	/*	Arrays.sort(arr,new Comparator<Person>() {

			@Override
			public int compare(Person o1, Person o2) {
				// TODO Auto-generated method stub
				return o1.getAge()-o2.getAge();
			}
		});*/
		
		//使用Lambda表达式,计划
		Arrays.sort(arr,(Person o1,Person o2)->{
			return o1.getAge()-o2.getAge();
		});
		//优化
		Arrays.sort(arr,(Person o1,Person o2)->o1.getAge()-o2.getAge());
		
		
		//遍历
		for (Person person : arr) {
			System.out.println(person);
		}
	}
}

-----------------------------
public class Person {

	private String name;
	private int age;
	
	public Person() {
		super();
	}
	
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	
}

----------------------------
public class RunnableImpl implements Runnable{

	@Override
	public void run() {
		//获取线程名称
		System.out.println(Thread.currentThread().getName()+"创建新的线程");
		
	}

}

----------------------
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 	JDK1.5之后提供的
 * 	线程池-->容器 -->集合 (ArrayList,HashSet,LinkedList,HashMap)
 * 		当线程第一次启动的时候,创建多个线程,保存到集合当中,当我们要使用线程的时候
 *	就可以从集合中取出来使用
 * 		当我们使用完毕需要江县城归还给线程池
 * 		
 * 	java.util.concurrent.Executors
 * 		Executors类生产线程池的工厂类
 * 			static ExecutorSerevice newFixedThreadPool(int nThread)
 * 			创建一个可重用固定线程数的线程池
 * 			参数:线程数量
 * 			返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象
 * 		我们使用ExecutorService接口接收(面向接口编程)
 * 		java.util.concurrent.ExecutorService:线程池接口
 * 			submit(Runable task) 提交一个Runable接口任务用于自行
 * 		   关闭销毁线程池:
 * 			shutdown();
 * 
 * 		线程池的使用:
 * 			使用工厂类里面的方法生产线程
 * 			创建一个类实现Runable接口,重写run方法,设置任务
 * 			调用ExecutorService中的方法submit,传递线程任务实现类,开启线程
 * 			调用shutdown方法销毁线程池,不建议执行
 * 
 * 
 * 
 */
public class XianChengPool {
	public static void main(String[] args) {
		//获取
		ExecutorService es = Executors.newFixedThreadPool(2);
		es.submit(new RunnableImpl());
		es.submit(new RunnableImpl());
		//有一个线程等着,等归还完之后继续使用,线程池会一直开启,使用完之后自动归还
		es.submit(new RunnableImpl());
		//销毁线程池
		es.shutdown();
		//没有线程池,不能在执行线程池后面的代码
//		es.submit(new RunnableImpl());//抛异常
	}

}