上篇博客回顾:
1.什么是多线程安全
当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。做读操作是不会发生数据冲突问题。
2.synchronized用法
2.1、在可能发生线程安全问题的代码用synchronized(同一个锁数据){包括起来}
2.2、在方法上修饰synchronized称为同步函数
2.3、在方法上加上static关键字,使用synchronized 关键字修饰
3.死锁
同步中嵌套同步,导致锁无法释放
这篇博客记录线程通讯学习记录
1.多线程之间如何通讯
2.wait、notify、notifyAll()方法
3.lock
4.停止线程
5.守护线程
6.Join方法
7.优先级
一、什么是多线程之间通讯?
代码实现
第一建立共享实体类
建立输入线程
建立输出线程
在main方法里面新建启动
运行
数据发生错乱,造成线程安全问题解决办法在两个线程中加入同步代码块
输入线程
输出线程
注意 同步代码块中的同步锁应该使用的是当前实体类 不然的话达不到同步的效果
运行
代码错乱的问题是解决了但是现在有一个新的问题就是没有达到预想的效果 我们想要的效果应该是读一个写一个操作。
在解决之前我们先来看看wait()、notify、notifyAll()方法
二、wait()、notify、notifyAll()
wait()、notify、notifyAll()定义在Object类中方法,可以用来控制线程的状态这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
1.如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
2. 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
3.如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
我们现在实体类中添加一个标识
之后在输入线程中根据标识使用wait() 和notify()方法
输出线程同理
之后运行走起
解析:当标识为false时输入线程开始输入,把值输入之后修改标识为true 。当输出线程发现标识为true的时候开始读取值,当值输入完成之后把标识改为false。以此循环
wait与sleep区别?
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
获取对象锁进入运行状态。
三、lock
3.1 、Lock写法:
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能会出现线程安全的操作
}finally{
//一定在finally中释放锁
//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
lock.ublock();
}
3.2 、ReentrantLock写法:
ReentrantLock是一个可重入的互斥锁,ReentrantLock由最近成功获取锁,还没有释放的线程所拥有,当锁被另一个线程拥有时,调用lock的线程可以成功获取锁。如果锁已经被当前线程拥有,当前线程会立即返回
3.3、Lock 接口与 synchronized 关键字的区别
Lock 接口可以尝试非阻塞地获取锁当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
Lock 接口能被中断地获取锁与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。
3.4、Condition用法
Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能,
代码:
Condition condition =lock.newCondition();
res. condition.await(); 类似wait
res. Condition.Signal() 类似notify
Signalall notifyALL
案列:
写入线程
读入线程
四、如何停止线程
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。
3. 使用interrupt方法中断线程。线程在阻塞状态
案列一 使用标识停止线程
案列二 使用interrupt中断线程
运行结果
五、守护线程
Java中有两种线程,一种是用户线程,另一种是守护线程。
当进程不存在或主线程停止,守护线程也会被停止。
使用setDaemon(true)方法设置为守护线程
六、join()方法的作用
join作用是让其他线程变为等待
案列 创建三个线程
直接运行的话会出现交叉执行 也就是线程抢占CPU执行时间导致
添加Joni方法之后
七、优先级
现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5
案列
注意:设置了优先级,不代表每次都一定会被执行。只是CPU调度会有限分配
八、Yield方法
Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)
yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。