多线程

什么是线程?

程序内部的一条执行路径.

一个程序内部可以有多条执行路径,那这个程序就是多线程程序.

一个程序内部如果只有一条执行路径,那这个程序就是单线程程序.

并发和并行:

并行:同一时刻,多个指令被多个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.什么是生产者消费者模式

生产者消费者模式是一个十分经典的多线程协作的模式

所谓生产者消费者问题,实际上主要是包含了两类线程:

  • 生产者生产数据

生产者逻辑:

  1. 判断桌子上是否有汉堡包

    • 有,等待

    • 没有,生产

  2. 把汉堡包放在桌子上

  3. 叫醒等待的消费者开吃

  • 消费者消费数据

消费者逻辑

  1. 判断桌子上是否有汉堡包

  2. 如果没有就等待

  3. 如果有就开吃

  4. 吃完之后,桌子上的汉堡包就没有了

    • 叫醒等待的生产者继续生产

    • 汉堡包的总数量减一

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();
}
}
}
}
}
}




posted on 2023-03-22 14:28  zl子路  阅读(16)  评论(0)    收藏  举报