等待唤醒机制案例
//包子类
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 将一个接口规范为函数式接口