
(以下内容全部来自上述课程)
多线程
1. Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
手动上锁、手动释放锁
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作 Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化 ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例

2. 死锁(错误)
锁的嵌套容易导致死锁
3. 等待唤醒机制(生产者和消费者)
生产者消费者模式是一个十分经典的多线程协作的模式。
3.1 消费者:消费数据----消费者等待

- Desk:
java
package com.itheima.a13waitandnotify;
public class Desk {
/*
*作用:控制生产者和消费者的执行*
* */
//是否有面条0:没有面条1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static 0bject lock = new 0bject();
}
- Foodie:
java
package com.itheima.a13waitandnotify;
public class Foodie extends Thread{
@Override
public void run(){
/*
* 1.循环
* 2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾)
* 4.判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)**/
while(true){
synchronized (Desk.lock){
if(Desk.count ==0){
break;
}else{
//先判断桌子上是否有面条
if(Desk.foodFlag == 0){
//如果没有,就等待
try{
Desk.lock.wait();//让当前线程跟锁进行绑定
} catch (InterruptedException e) {
e.printStackTrace();
} I
}else{
//把吃的总数-1
Desk.count--;
//如果有,就开吃
System.out.println("吃货在吃面条,还能再吃"+ Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做
Desk. Lock.notifyA11();
//修改桌子的状态
Desk.foodFLag =0;
}
}
}
}
}
}
3.2 生产者:生产数据----生产者等待

- Cook
java
package com.itheima.a13waitandnotify;
public class Cook extends Thread{
@Override
public void run(){
/*
* 1.循环
*2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾)
* 4.判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)* */
while (true){
synchronized (Desk.Lock){
if(Desk.count ==0){
break;
}else{
//判断桌子上是否有食物
if(Desk.foodFlag == 1){
//如果有,就等待
try {
Desk.Lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//如果没有,就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFLag = 1;
//叫醒等待的消费者开吃
Desk.ymck.notifyA11();
}
}
}
}
}
}
- 测试类
java
package com.itheima.a13waitandnotify;
public class ThreadDemo {
public static void main(String[] args) {
/*
*
* 需求:完成生产者和消费者(等待唤醒机制)的代码
* 实现线程轮流交替执行的效果
*
* */
//创建线程的对象
Cook c = new Cook();
Foodie f = new Foodie();
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
3.3 阻塞队列方式实现
- TreadDemo:
java
package com.itheima.a14waitandnotify;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args){
/*
*
* 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
细节:
* 生产者和消费者必须使用同一个阻塞队列
*
* */
//1.创建阻塞队列的对象
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(capacity: 1);
//2.创建线程的对象,并把阻塞队列传递过去
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
//3.开启线程
c.start();
f.start();
}
}
- Cook:
java
package com.itheima.a14waitandnotify;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while(true){
//不断的把面条放到阻塞队列当中
try {
queue.put( e: "面条");
System.out.println("厨师放了一碗面条")
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- Foodie:
java
package com.itheima.a14waitandnotify;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while(true){
//不断从阻塞队列中获取面条
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. 线程的状态
Java中没有定义运行 状态。
实际上:
5. 线程池
一个存放线程的容器。
5.1 主要核心原理
-
创建一个池子,池子中是空的
-
提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
-
但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
5.2 方法

java
public class MyThreadPoolDemo {
public static void main(String[] args) {
/*
public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool (int nThreads)创建有上限的线程池*/
//1.获取线程池对象
ExecutorService pool1= Executors.newCachedThreadPool();
//2.提交任务
pool1.submit(new MyRunnable()); pool1.submit(new MyRunnable());|
//3.销毁线程池
//pool1.shutdown();
}
}
5.3 自定义线程池

队伍满了之后才会启用临时线程,直接执行没在队伍内的任务。
超出核心线程+临时线程+队伍长处加起来的和的任务,通常会触发任务拒绝策略。
java
/*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数一:核心线程数量 不能小于0
参数二:最大线程数 不能小于0,最大数量>=核心线程数量
参数三:空闲线程最大存活时间 不能小于0
参数四:时间单位 用TimeUnit指定
参数五:任务队列 不能为nu11
参数六:创建线程工厂 不能为nu11
参数七:任务的拒绝策略 不能为nu11 */
ThreadPoolExecutor pool = new ThreadPoolExecutor(
corePoolSize: 3, //核心线程数量,能小于0
maximumPoolSize: 6, //最大线程数,不能小于0,最大数量>=核心线程数量
keepAliveTime: 60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(capacity: 3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
);
- 创建一个空的池子
- 有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程
不断的提交任务,会有以下三个临界点:
- 当核心线程满时,再提交任务就会排队
- 当核心线程满,队伍满时,会创建临时线程
- 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
6.最大并行数
与CPU有关
4核8线程:
- 4核:4个大脑,可以同时干4件事 -->超线程技术 --> 8线程
- 8线程:最大并行数为8
7. 线程池多大合适
- CPU密集型运算:计算比较多,读取本地文件或数据库较少 --> 最大并行数+1
- I/O密集型运算:读取本地文件或数据库比较多(大多数都是此类)
公式举例: