导图社区 5.多线程
java多线程知识思维导图,主要内容有线程周期、线程安全处理、线程通信、生产者和消费者模式、线程的优先级。
编辑于2022-04-01 14:12:12中心主题
线程周期
子主题
线程安全处理
同步代码块
格式: synchronized(同步监视器){ //需要被同步的代码 }
1.操作共享数据的代码,即需要被同步的代码
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁 要求:多个线程必须共用同一把锁。
在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当监视器 在继承Thread类的创建多线程方式中,可以考虑使用 类名.calss 充当监视器,类本身是一个对象即Class的对象,类自会加载一次 (类也是一个对象)
锁多条语句操作共享数据,可以使用同步代码块
同步的好处弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行的效率
同步方法
1. 同步synchronised方法仍然涉及到同步监视器,只是不需要我们显示的声明。
2. 非静态的同步方法,同步监视器是:this 静态的同步方法,同步监视器是:当前类本身
概述:就是把synchronized关键字加到方法上 格式: 修饰符 synchroniaze 返回值类型 方法名(方法参数){ }
同步方法锁的对象使this
同步静态方法:就是把synchronized关键字加到静态方法上 格式:修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法的锁对象是 类名.clss
lock锁
lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
synchronised与lock锁的异同点: synchronised机制在执行完相应的同步代码以后,自动释放同步监视器 lock需要手动的启动同步lock(),同时结束需要手动unlock()
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5之后提供了一个新的锁对象问题
lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock是接口不能直接实列化,这里采用它的实现类ReentrantLock来实列化
ReentrantLock():创建一个reentrantLock的实列
线程安全的类
StringBuffer 约等于StringBuilder
vector 约等于ArrayList
Hashtable 约等于 Hashmap
子主题
线程通信
wait():一旦执行次方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程就唤醒优先级高的线程
notifyAll():一旦执行此方法,就会唤醒所以被wait的线程
wait(),notify(),notifyAll(),三个方法必须使用在同步代码块或同步方法中
wait(),notify(),notifyAll(),三个方发调用者必须是和同步监视器相同
wait(),notify(),notifyAll(),三个方法是Object类的方法
生产者和消费者模式
为了体现生产者和消费者过程中的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法造Object类中
void wait():导致当前线程等待,直到另一个线程调用该对象的notif()方法或nitifyAll()方法
void nitify():唤醒正在等待对象监视器的的单个线程
void nitifyAll():唤醒正在等待对象监视器的所有线程
线程的优先级
MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
getPriority():获取线程的优先级
setPriority():设置线程的优先级
Thread常用方法
线程控制
static void sleep(long millis):让当前线程阻塞指定的毫秒
void join():在线程a中调用线程b的join()方法,此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
void setDaemon(boolean on):将此线线程标记为守护线程,当运行的线程都是守护线程时,虚拟机退出。
public static currentThred()
静态方法,返回当前代码的线程
start():启动当前线程,并调用当前线程的run()方法
yield():释放当前cpu的执行权
Thread类中设置和获取线程名称的方法 设置和获取线程名称
void setName(String name):将此线程的名称更改为等于参数name
String getName():返回此线程的名称
通过构造方法也可以设置线程的名称

sleep()与wait()的异同
相同点:一旦执行方法,都可以使当前线程进入阻塞状态
不同点:
1. 两个方法声明的位置不同,Thread类中声明slepp(),Object类中声明wait()
2. 调用的要求不同,sleep()可以在任何需要的场景下调用,wait()必须使用在synchornised中
3. wait在阻塞线程的同时,还可释放同步监视器
多线程的实现方法:
方式一继承自Thread类
1.创建一个类MyThread继承自Thread类 2.在MyThread类中重写Threa类的run()方法 3.在主线程内创建Thread子类的实列对象 4.调用start()方法
为什么要重写run()方法? 因为run()是用来封装被线程执行的代码
子主题
run()方法:封装线程执行的代码,直接调用,相当于普通方法的调用
start():①启动线程;②然后由JVM调用此线程的run()方法
方式二实现Runnable接口
1.创建一个实现Runnable接口的类MyRunnabole
2.在实现类MyRunnable类中重写Runnable的抽象方法run()
3.创建实现类MyRunnable类的对象
4.创建Thread类的对象,把MyRunnable对象作为Thread构造方法的参数
5.通过Thread类的对象调用start()
方式三实现Callable接口
1.创建一个实现Callable的实现类 2.实现Call()方法,将此线程需要执行的操作声明在call()中 3.创建Callable接口实现类的对象 4.将此Callable接口实现类对象作为值传递到FutureTask构造器中,创建FutureTask的对象 5.将FutureTask 的对象作为参数传递到Thread类的构造器中,创建Thread对象,调用start()方法 6.futuretask.get()的值为Callable实现类重写的call()返回值
方式四:线程池
子主题
子主题
子主题
相比继承Thread类,实现Runnable接口的好处
1.避免了Java单继承的局限性 2.适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码,数据有效分离,较好的体现了面对像的设计思想
子主题
概述
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程
进程
概述:是正在运行的程序
1.是系统进行资源分配和调用的独立单位
2.每一位进程都有它自己的内存空间和系统资源
线程调度
线程调度的两种模型
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选一个,优先级高的线程获取的CPU时间片相对多一些,
Thread类中设置和获取线程优先级的方法
public final int getPriority():返回此线程的优先级
public final void setPriority(int newPriority):更改此线程的优先级
线程默认优先级是5;线程优先级的范围是:1-10 线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果