多线程
什么是线程?
程序内部的一条执行路径.
一个程序内部可以有多条执行路径,那这个程序就是多线程程序.
一个程序内部如果只有一条执行路径,那这个程序就是单线程程序.
并发和并行:
并行:同一时刻,多个指令被多个cpu同时执行
并发:同一时刻,多个质量被一个cpu交替执行
进程和线程
进程是正在运行的程序
线程是程序内部的一条执行路径.
一个程序内部可以有多条执行路径,那这个程序就是多线程程序.
一个程序内部如果只有一条执行路径,那这个程序就是单线程程序.
多线程的三种方式
-
继承Thread类
流程:
1.创建子类继承Thread父类
2.重写run方法
3.创建子类对象
4.调用start()方法
优点:简单
缺点:扩展性差,没有返回值
-
实现Runnable接口
流程:
1.创建实现类实现Runnable接口
2.重写run方法
3.创建实现类对象
4.传入Thread构造
5.调用start()方法
优点:扩展性强
缺点:没有返回值
-
实现Callable接口
流程:
1.创建实现类实现Callable接口
2.重写run方法
3.创建实现类对象
4.把实现类对象封装成FutureTask对象
4.把FutureTask对象传入Thread构造
5.调用start()方法
6.创建相应包装接收返回值
优点:扩展性强,有返回值
缺点:编程复杂
常用方法
1.start()启动线程
2.getName()获取线程名称
3.setName()更改线程名称
4.currenThread()获取当前线程对象的引用
5.sleep()线程休眠
对线程出现的三个安全问题?
1.对线程环境
2.访问共享资源
3.多条语句访问共享资源
4.解决: 从第三个原因入手,把可能产生安全问题的代码,锁起来,变成一个整体,一次只让一个线程执行.
方式一: 同步代码块 ,锁对象必须唯一,建议使用共享资源
-
-
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
方式二: 同步方法 实例方法的锁是this,静态同步方法的锁是当前类名.class
方式三: Lock
5.锁不要嵌套,会导致死锁
生产者消费者模式
1.什么是生产者消费者模式
生产者消费者模式是一个十分经典的多线程协作的模式
所谓生产者消费者问题,实际上主要是包含了两类线程:
- 生产者生产数据
生产者逻辑:
-
-
有,等待
-
没有,生产
-
-
把汉堡包放在桌子上
-
叫醒等待的消费者开吃
- 消费者消费数据
消费者逻辑
-
-
如果没有就等待
-
如果有就开吃
-
吃完之后,桌子上的汉堡包就没有了
-
叫醒等待的生产者继续生产
-
汉堡包的总数量减一
-
2.常用方法
wait()线程等待
notify()随机唤醒一个线程
notifyAll()唤醒所有线程
3.实现步骤
注意要使用锁对象来调用等待唤醒方法
1.控制生产消费
设置结束条件
创建一个锁对象
public class Desk {
//0=没有面条 1=有面条
public static int foodFlag=0;
//结束条件
public static int count=10;
//创建一个锁对象
public static Object lock=new Object();
public static void main(String[] args) {
Cook cook = new Cook();
Foodie foodie = new Foodie();
cook.start();
foodie.start();
}
}
2.生产者:
创建类继承Thread
重写run方法
循环
同步代码块
判断共享资源是否到末尾(到末尾)
判断共享资源是否到末尾(没到末尾,具体逻辑)
public class Cook extends Thread{
/*
* //循环
* //同步代码块
* //判断共享资源是否到末尾(到末尾)
* //判断共享资源是否到末尾(没到末尾,执行核心逻辑)
*
*
* */
@Override
public void run() {
while(true){
synchronized (Desk.lock){
if (Desk.count==0){
System.out.println("你已经吃完");
break;
}else{
if(Desk.foodFlag==1){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//判断是否有食物
//有食物等待
//无食物,做食物,唤醒
Desk.foodFlag=1;
System.out.println("厨师做了一碗面条,唤醒厨师吃面");
Desk.lock.notifyAll();
}
}
}
}
}
}
3.消费者:
创建类继承Thread
重写run方法
循环
同步代码块
判断共享资源是否到末尾(到末尾)
判断共享资源是否到末尾(没到末尾,具体逻辑)
public class Foodie extends Thread{
@Override
public void run() {
//循环
//同步代码块
//判断共享数据是否到末尾(到末尾)
//判断共享数据是否到末尾(没到末尾,执行核心逻辑)
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
}else{//判断有没有面条(没苗条,等待)
if (Desk.foodFlag==0){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{ //总次数减一
//有面条开吃
//唤醒厨师
Desk.count--;
System.out.println("开吃,你还可以吃"+Desk.count+"碗面条");
System.out.println("吃完,唤醒厨师");
Desk.lock.notifyAll();
}
}
}
}
}
}
浙公网安备 33010602011771号