day16. 多线程
java
课前回顾:
1.包装类:基本类型对应的包装类
2.Byte Short Integer Long Float Double Character Boolean
3.装箱:
valueOf()
4.拆箱:
intValue()
5.实现多线程方式:
继承Thread
实现Runnable
实现Callable
线程池
6.Thread中的方法:
run():设置线程任务
start():开启线程,jvm会自动执行run方法
getName():获取线程名字
setName(String name):设置线程名字
sleep(long time):线程睡眠
currentThread():获取当前正在执行的线程对象
7.同步代码块:
synchronized(锁对象){
}
8.同步方法:在方法声明上加上synchronized关键字
非静态的默认锁:this
静态的默认锁:当前类.class
今日重点:
1.会使用wait和notify实现一个简单的等待唤醒案例
2.知道sleep和wait区别
3.手撕单例模式
4.会使用Lambda表达式
第一章.线程状态
1.线程状态介绍
java
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用 start 方法。 |
Runnable(可运行) | 线程可以在 java 虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态;当该线程持有锁时,该线程将变成 Runnable 状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用 notify 或者 notifyAll 方法才能够唤醒。 |
Timed Waiting(计时等待) | 同 waiting 状态,有几个方法有超时参数,调用他们将进入 Timed Waiting 状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait。 |
Terminated(被终止) | 因为 run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡。或者调用过时方法 stop() |
2.线程状态图

第二章.等待唤醒机制
1.等待唤醒案例分析(线程之间的通信)
java
要求:一条线程生产,一条线程消费,不能连续生产,不能连续消费 -> 线程的通信
方法 | 说明 |
---|---|
wait() | 无线等待 |
notify() | 唤醒一条等待线程 |
notifyAll() | 唤醒所有等待线程 |
2.等待唤醒案例实现

java
public class BaoZiPu {
private int count;
private boolean flag;
public BaoZiPu() {
}
public BaoZiPu(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
/**
* get方法专门为消费线程服务
*/
public void getCount() {
System.out.println("消费了第......"+count+"个包子");
}
/**
* set方法专门为生产线程服务
*/
public void setCount() {
count++;
System.out.println("生产了第..."+count+"个包子");
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
java
/**
* 生产者
*/
public class Product implements Runnable {
private BaoZiPu baoZiPu;
public Product(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (baoZiPu) {
//1.如果flag为true,证明有包子,生产线程等待
if (baoZiPu.isFlag() == true) {
try {
baoZiPu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为false,证明没有包子,就要生产包子
baoZiPu.setCount();
//3.修改flag状态为true,证明有包子了
baoZiPu.setFlag(true);
//4.唤醒消费线程
baoZiPu.notify();
}
}
}
}
java
/**
* 消费者
*/
public class Consumer implements Runnable {
private BaoZiPu baoZiPu;
public Consumer(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (baoZiPu) {
//1.如果flag为false,证明没有包子,消费线程等待
if (baoZiPu.isFlag() == false) {
try {
baoZiPu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为true,证明有包子,就要消费包子
baoZiPu.getCount();
//3.修改flag状态为false,证明没有包子了
baoZiPu.setFlag(false);
//4.唤醒生产线程
baoZiPu.notify();
}
}
}
}
java
public class Test01 {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
Product product = new Product(baoZiPu);
Consumer consumer = new Consumer(baoZiPu);
Thread t1 = new Thread(product);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}

3.用同步方法改造等待唤醒案例
java
public class BaoZiPu {
private int count;
private boolean flag;
public BaoZiPu() {
}
public BaoZiPu(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
/**
* get方法专门为消费线程服务
*/
public synchronized void getCount() {
//1.如果flag为false,证明没有包子,消费线程等待
if (this.flag == false) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为true,证明有包子,就要消费包子
System.out.println("消费了第......"+count+"个包子");
//3.修改flag状态为false,证明没有包子了
this.flag = false;
//4.唤醒生产线程
this.notify();
}
/**
* set方法专门为生产线程服务
*/
public synchronized void setCount() {
//1.如果flag为true,证明有包子,生产线程等待
if (this.flag == true) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为false,证明没有包子,就要生产包子
count++;
System.out.println("生产了第..."+count+"个包子");
//3.修改flag状态为true,证明有包子了
this.flag = true;
//4.唤醒消费线程
this.notify();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
java
/**
* 生产者
*/
public class Product implements Runnable {
private BaoZiPu baoZiPu;
public Product(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
baoZiPu.setCount();
}
}
}
java
/**
* 消费者
*/
public class Consumer implements Runnable {
private BaoZiPu baoZiPu;
public Consumer(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
baoZiPu.getCount();
}
}
}
java
public class Test01 {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
Product product = new Product(baoZiPu);
Consumer consumer = new Consumer(baoZiPu);
Thread t1 = new Thread(product);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
2.多等待多唤醒
2.1.解决多生产多消费问题(if 改为 while,将 notify 改为 notifyAll)
java
public class BaoZiPu {
private int count;
private boolean flag;
public BaoZiPu() {
}
public BaoZiPu(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
/**
* get方法专门为消费线程服务
*/
public synchronized void getCount() {
//1.如果flag为false,证明没有包子,消费线程等待
while (this.flag == false) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为true,证明有包子,就要消费包子
System.out.println("消费了第......"+count+"个包子");
//3.修改flag状态为false,证明没有包子了
this.flag = false;
//4.唤醒等待线程
this.notifyAll();
}
/**
* set方法专门为生产线程服务
*/
public synchronized void setCount() {
//1.如果flag为true,证明有包子,生产线程等待
while (this.flag == true) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2.如果flag为false,证明没有包子,就要生产包子
count++;
System.out.println("生产了第..."+count+"个包子");
//3.修改flag状态为true,证明有包子了
this.flag = true;
//4.唤醒等待线程
this.notifyAll();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
java
/**
* 消费者
*/
public class Consumer implements Runnable {
private BaoZiPu baoZiPu;
public Consumer(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
baoZiPu.getCount();
}
}
}
java
/**
* 生产者
*/
public class Product implements Runnable {
private BaoZiPu baoZiPu;
public Product(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
baoZiPu.setCount();
}
}
}
java
public class Test01 {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
Product product = new Product(baoZiPu);
Consumer consumer = new Consumer(baoZiPu);
new Thread(product).start();
new Thread(product).start();
new Thread(product).start();
new Thread(consumer).start();
new Thread(consumer).start();
new Thread(consumer).start();
}
}
第三章.设计模式
java
设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性,稳定性。
1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。<大话设计模式>
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1.单例模式
properties
目的:让一个类只产生一个对象,供外界使用
单:一个
例:实例,对象
1.1.饿汉式:
properties
我好饿呀,想马上要一个对象,迫不及待的想要一个对象
既然想马上来个对象,我们就需要让这个对象赶紧new出来,尽早new出来
java
public class Singleton {
/**
* 由于我们一个类只产生一个对象
* 所以我们就不能让外界随便new
* 所以我们需要将构造方法私有化
*/
private Singleton() {
}
/*
由于是饿汉式,需要我们自己赶紧new一个对象出来
所以我们需要在new的时候,将其变成静态的
又由于我们new出来的对象不能随便让外界通过类名调用
所以将其再变成private的
*/
private static Singleton singleton = new Singleton();
/**
* 定义一个公共的接口,将内部的对象返回给外界
*/
public static Singleton getInstance() {
return singleton;
}
}
1.2.懒汉式:
properties
不着急new对象,啥时候用,啥时候new,也要保证一个类只产生一个对象
java
public class Singleton2 {
/**
* 由于我们一个类只产生一个对象
* 所以我们就不能让外界随便new
* 所以我们需要将构造方法私有化
*/
private Singleton2() {
}
/*
由于是懒汉式,不着急new对象了
*/
private static Singleton2 singleton = null;
/**
* 定义一个公共的接口,将内部的对象返回给外界
*/
public static Singleton2 getInstance() {
if (singleton == null){
synchronized (Singleton2.class){
if (singleton == null){
singleton = new Singleton2();
}
}
}
return singleton;
}
}
第四章.Lambda 表达式
1.函数式编程思想和 Lambda 表达式定义格式
java
1.面向对象思想:讲究的是先找对象,然后调用对象中的方法 -> 过于强调找对象这个过程
好比是:去成都 -> 过于强调怎么去
2.函数式编程思想:jdk8开始有的 -> 强调的是目的,结果,不强调过程
比如:去成都 -> 强调的是去了还是没去
java
public class Demo01Lambda {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是线程1");
}
}).start();
System.out.println("======================");
//Lambda表达式
new Thread(()-> System.out.println("我是线程2")).start();
}
}
2.Lambda 表达式使用前提
java
1.必须是函数式接口作为方法参数传递
2.函数式接口:
有且只有一个抽象方法的接口
java
public interface USB {
void open();
}
java
public class Demo02Lambda {
public static void main(String[] args) {
method(new USB() {
@Override
public void open() {
System.out.println("USB打开");
}
});
System.out.println("==========================");
method(()-> System.out.println("USB打开"));
}
public static void method(USB usb){
usb.open();
}
}
3.Lambda 表达式省略规则
java
涛哥秘籍:初学者怎么学
1.先观察是否是函数式接口做方法参数传递
2.如果是,考虑使用lambda表达式
3.调用方法,实参以匿名内部类方式传递
4.从new接口开始到重写方法的方法名结束选中,删除,别忘记再删除一个右半个大括号
5.在剩下的重写方法的参数和方法体之间,加上 ->
省略规则:
1.重写方法的参数类型可以省略
2.如果重写方法的参数只有一个,所在的小括号可以省略
3.如果重写方法的方法体中只有一句代码,所在的大括号以及分号可以省略
4.如果重写方法的方法体中只有一句代码,而且是带return的,那么所在的大括号,分号以及return关键字都可以省略
java
public interface USB {
public abstract String open(String s);
}
java
public class Test01 {
public static void main(String[] args) {
method(new USB() {
@Override
public String open(String s) {
return s+"打开了";
}
});
System.out.println("=======================");
method((String s)->{
return s+"打开了";
});
System.out.println("=======================");
method(s -> s+"打开了");
}
public static void method(USB usb){
String result = usb.open("鼠标");
System.out.println("result = " + result);
}
}
第五章.函数式接口
java
1.概述:有且只有一个抽象方法的接口
2.检测注解:@FunctionalInterface -> 在接口上写
1.Supplier
java
1.Supplier接口
java.util.function.Supplier<T>接口,它意味着"供给"->我们想要什么就给什么
2.方法:
T get() -> 我们想要什么,get方法就可以返回什么
3.需求:
使用Supplier接口作为方法的参数
用Lambda表达式求出int数组中的最大值
4.泛型:
<引用数据类型>-> 规定了我们操作的数据是什么类型
<>中只能写引用数据类型,不能写基本数据类型
泛型的作用就是为了统一类型
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
java
public class Demo01Supplier {
public static void main(String[] args) {
method(new Supplier<Integer>() {
@Override
public Integer get() {
int[] arr = {4,3,4,56,7,5,4};
Arrays.sort(arr);
return arr[arr.length-1];
}
});
System.out.println("=========================");
method(()->{
int[] arr = {4,3,4,56,7,5,4};
Arrays.sort(arr);
return arr[arr.length-1];
});
}
public static void method(Supplier<Integer> supplier){
Integer element = supplier.get();
System.out.println("element = " + element);
}
}

2.Consumer
java
java.util.function.Consumer<T>->消费型接口->操作
方法:
void accept(T t),意为消费一个指定泛型的数据
"消费"就是"操作",至于怎么操作,就看重写accept方法之后,方法体怎么写了
java
public class Demo02Consumer {
public static void main(String[] args) {
method(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s.length());
}
},"abcdefg");
System.out.println("=======================");
method(s-> System.out.println(s.length()),"abcdefg");
}
public static void method(Consumer<String> consumer,String s){
consumer.accept(s);
}
}

3.Function
java
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据
方法:
R apply(T t)根据类型T参数获取类型R的结果
java
public class Demo03Function {
public static void main(String[] args) {
method(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer+"";
}
},100);
System.out.println("=======================");
method(integer-> integer+"",100);
}
public static void method(Function<Integer,String> function,Integer i){
String s = function.apply(i);
System.out.println("s = " + s);
}
}
4.Predicate
java
java.util.function.Predicate<T>接口。->判断型接口
boolean test(T t)->用于判断的方法,返回值为boolean型
java
public class Demo04Predicate {
public static void main(String[] args) {
method(s -> s.endsWith("g"),"abcdefg");
}
public static void method(Predicate<String> predicate,String s){
boolean result = predicate.test(s);
System.out.println("result = " + result);
}
}