线程的优先级
public static void main(String[] args){
Thread thread = new Thread();
thread.start();
int ui_proi = Process.getThreadPriority(0)
int th_proi = thread.getPriority();
//输出结果
ui_proi = 5
th_proi = 5
}
1.线程的优先级是具有继承性的,在某个线程中创建的线程会继承线程的优先级。例如我们在UI线程中创建了线程,则线程优先级和UI线程优先级一样,平等的和UI线程抢占CPU时间资源。
2.JDKAPI 中限制了新设置的线程优先级必须为[1~10], 优先级priority越高,获取CPU时间片段的概率越高。
java.lang.Thread.setPriority(int newPriority)
3.Android API 可以为线程设置更加精细的优先级(-20~19)优先级priority的值越低,获取CPU时间片的概率越高。
android.os.Process.setThreadPriority(int newPriority)
线程的状态
NEW 初始状态,线程被新建,还没调用start方法
RUNNABLE 运行状态,把“运行中”和“就绪”统称为运行状态
BLOCKED 阻塞状态,表示线程阻塞于锁
WAITING 等待状态,需要其他线程通知唤醒
TIME_WAITING 超时等待状态,表示可以在指定的时间超时后自行返回
TERRMINATED 终止状态,表示当前线程已执行完毕
下图表示线程各种状态间的切换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o4xaTXBc-1665970531026)(?)]
wait 线程进入等待
wait关键字能使线程进入等待,释放线程锁
/**================================================================
* wait关键字
* wait关键字能使线程进入等待,释放线程锁,理想状态下thread1应该先执行,执行进入wait方法后释放线程锁,thread得以继续向下执行,唤醒线程1继续执行
* 日志应该为
* RUN1---start
* RUN2---start
* RUN1---end
* RUN2---end
* 但是无法确定线程1或线程2谁先执行,若线程2 先执行,线程1执行wait方法后将进入等待且无人唤醒,所以需要加原子变量控制
* 实际日志
*
D/ThreadTestForJava: RUN2---start1644565101644
D/ThreadTestForJava: RUN2---end1644565101644
D/ThreadTestForJava: RUN1---start1644565101645
* ================================================================
*/
Object obj = new Object();
class Runable1 implements Runnable{
@Override
public void run() {
Log.d(TAG, "RUN1---start" + System.currentTimeMillis());
synchronized (obj){
try {
if(isWait){
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, "RUN1---end" + System.currentTimeMillis());
}
}
class Runable2 implements Runnable{
@Override
public void run() {
Log.d(TAG, "RUN2---start" + System.currentTimeMillis());
synchronized (obj){
isWait = false;
obj.notify();
}
Log.d(TAG, "RUN2---end" + System.currentTimeMillis());
}
}
public void waitTest(){
Thread thread1 = new Thread(new Runable1());
Thread thread2 = new Thread(new Runable2());
thread1.start();
thread2.start();
}
join关键字
join关键字的作用是等待目标线程执行完后再执行此线程
/**
* ===============================================================================
* join关键字
* 等待目标线程执行完后在执行此线程
* 正常情况先,runable4应该先执行完,然后runable3会等待10秒后才能走完,在runable4中调用join方法,runable4将会等待runable3走完才继续执行
* 未加入join
* D/ThreadTestForJava: RUN4---start1644566194613
* D/ThreadTestForJava: RUN4---end1644566194614
* D/ThreadTestForJava: RUN3---start1644566194614
* D/ThreadTestForJava: RUN3---end1644566204616
* 加入join
* D/ThreadTestForJava: RUN3---start1644566395452
* D/ThreadTestForJava: RUN4---start1644566395453
* D/ThreadTestForJava: RUN3---end1644566405455
* D/ThreadTestForJava: RUN4---end1644566405458
* ===============================================================================
*/
Thread thread3 = new Thread(new Runable3());
Thread thread4 = new Thread(new Runable4());
class Runable3 implements Runnable{
@Override
public void run() {
Log.d(TAG, "RUN3---start" + System.currentTimeMillis());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "RUN3---end" + System.currentTimeMillis());
}
}
class Runable4 implements Runnable{
@Override
public void run() {
Log.d(TAG, "RUN4---start" + System.currentTimeMillis());
try {
thread3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "RUN4---end" + System.currentTimeMillis());
}
}
public void joinTest(){
thread3.start();
thread4.start();
}
sleep关键字
/**
* sleep关键字
* sleep关键字使调用线程进入休眠状态,但在一个synchronized中执行sleep,线程虽然会休眠,但是不会释放资源锁
* 若runable5先运行,则ruanable6要等到runable5sleep完成才能执行同步代码块中的代码
* D/ThreadTestForJava: RUN5---start1644567514833
* D/ThreadTestForJava: RUN5---end1644567524837
* D/ThreadTestForJava: RUN6---start1644567524838
* D/ThreadTestForJava: RUN6---end1644567524840
*/
class Runable5 implements Runnable{
@Override
public void run() {
Log.d(TAG, "RUN5---start" + System.currentTimeMillis());
synchronized (obj){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, "RUN5---end" + System.currentTimeMillis());
}
}
class Runable6 implements Runnable{
@Override
public void run() {
synchronized (obj){
Log.d(TAG, "RUN6---start" + System.currentTimeMillis());
Log.d(TAG, "RUN6---end" + System.currentTimeMillis());
}
}
}
public void sleepTest(){
Thread thread3 = new Thread(new Runable5());
Thread thread4 = new Thread(new Runable6());
thread3.start();
thread4.start();
}
主线程向子线程通信
/**
* ===============================================================
* 主线程向子线程通信
* 日志
* D/ThreadTestForJava: Thread: 123
* 子线程需要自己维护loop
* Looper.prepare();
* synchronized (HandlerThread.this){
* looper = Looper.myLooper();
* notify();
* }
* Looper.loop();
*为了防止looper为空,所以在获取loop的方法中加入wait,但是在主线程等待时间过长会引发RNA,所以先要调用start在去获取looper
* ===============================================================
*/
class HandlerThread extends Thread{
public HandlerThread(){
super("123");
}
private Looper looper;
public Looper getLooper() {
synchronized (HandlerThread.this){
if(looper == null && !isAlive()){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return looper;
}
@Override
public void run(){
Looper.prepare();
synchronized (HandlerThread.this){
looper = Looper.myLooper();
notify();
}
Looper.loop();
}
}
public void testHandlerThread(){
HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
Log.d(TAG, "Thread: " + Thread.currentThread().getName());
}
};
handler.sendEmptyMessage(1);
}
fun testAtomic(){
val runable = Runnable {
for (i in 0 until 10000){
ThreadSafeTest.addAtomic()
ThreadSafeTest.addVolatile()
}
}
val thread1 = Thread(runable)
val thread2 = Thread(runable)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
println("atomicCount ${ThreadSafeTest.atomicCount.get()}")
println("count ${ThreadSafeTest.count}")
}
线程锁与Volatile
java中线程锁可以根据功能分类为以下几种
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJ7PIavG-1665970531027)(?)]
其实主要就是 Synchronized 和 ReentrantLock
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1dWolq28-1665970531027)(?)]
Atomic
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOTXD9OE-1665970531028)(?)]
原子类和Volatile
原子类,CAS 实现无锁数据更新,自旋的设置能够有效避免线程因阻塞-唤醒带来的系统资源开销
适用场景:多线程计数,原子操作,并发数量小的场景
Volatile修饰的成员变量在每次线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存。
不能解决非原子操作的线程安全性。性能不及原子类高。
/**
*原子类和volatile修饰
* 原子类实现无锁更新,自旋的设计能够有效避免线程阻塞,唤醒带来的资源浪费
* 适用于多线程计数,原子操作,并发数量小,读操作多的场景
* volatile修饰的成员变量在每次线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫变化的值重新写入共享内存
* 非原子操作对其他线程不可见
*
* 在下面的代码中,两个线程分别对原子类和Volatile修饰的成员变量++,最后结果应该都是20000
* 但是Volatile修饰的成员变量最终结果却不是20000,这是因为其他线程无法拿到非原子操作后的值,导致自增出错
*
*
* atomicCount 20000
count 17974
*/
object ThreadSafeTest {
val atomicCount : AtomicInteger = AtomicInteger()
@Volatile
var count = 0
fun addAtomic(){
atomicCount.getAndIncrement()
}
fun addVolatile(){
count++
}
}
Synchronized线程锁
- 锁方法,加在方法上,未获取到对象锁的其他线程都不可以访问该方法。(如果多个实例还是会出现线程问题)
synchronized void printThreadName(){}
- 锁class对象,家在static方法上相当于给Class对象加锁,哪怕是不同的java对象实例,也需要排队执行
static synchronized void printThreadName(){}
- 锁代码块,未获取到对象锁的其他线程可以执行代码块之外的代码
void printThreadNam(){
String name = Thread.currentThread().getName();
System.out.print("线程" + name + "准备好了");
synchronized(this){
}
}
/**
*1.无任何线程安全的情况下,五个线程同时访问printTick
* 代码如下
* fun printTick(){
println("Thread_${Thread.currentThread().name}准备好了。。。")
Thread.sleep(1000)
println("Thread_${Thread.currentThread().name}买了${ticks.removeAt(0)}")
}
*
* 结果入下
Thread_Thread-0准备好了。。。
Thread_Thread-2准备好了。。。
Thread_Thread-4准备好了。。。
Thread_Thread-1准备好了。。。
Thread_Thread-3准备好了。。。
Thread_Thread-0买了第0张票
Thread_Thread-2买了第0张票
Thread_Thread-1买了第3张票
Thread_Thread-4买了第0张票
Thread_Thread-3买了第0张票
*出现重复购票
* 2.在方法上加了同步锁
@Synchronized
fun printTick(){
println("Thread_${Thread.currentThread().name}准备好了。。。")
Thread.sleep(1000)
println("Thread_${Thread.currentThread().name}买了${ticks.removeAt(0)}")
}
结果如下
Thread_Thread-0准备好了。。。
Thread_Thread-0买了第0张票
Thread_Thread-4准备好了。。。
Thread_Thread-4买了第1张票
Thread_Thread-3准备好了。。。
Thread_Thread-3买了第2张票
Thread_Thread-2准备好了。。。
Thread_Thread-2买了第3张票
Thread_Thread-1准备好了。。。
Thread_Thread-1买了第4张票
同步代码块中的代码值允许一个线程访问,这个锁只是对象锁,调用不同对象的同步方法,也会出现线程不安全的情况
for(i in 0 until 5){
Thread(Runnable {
SynchronizedTest().printTick()
}).start()
}
不同对象调用同步锁的代码
Thread_Thread-0准备好了。。。
Thread_Thread-3准备好了。。。
Thread_Thread-2准备好了。。。
Thread_Thread-1准备好了。。。
Thread_Thread-4准备好了。。。
Thread_Thread-0买了第0张票
Thread_Thread-2买了第0张票
Thread_Thread-4买了第0张票
Thread_Thread-3买了第0张票
Thread_Thread-1买了第0张票
无法起到线程安全
3.若同步锁放在静态方法上则会锁住class,在jvm中class只有一份,所以不同对象也会线程安全
4.若同步锁只作用与部分代码块,则不在同步代码块中的代码将无法保证线程安全,同步代码块之后的方法也可以获得线程安全
fun printTick(){
println("Thread_${Thread.currentThread().name}准备好了。。。")
synchronized(this){
Thread.sleep(1000)
println("Thread_${Thread.currentThread().name}买了${ticks.removeAt(0)}")
}
println("Thread_${Thread.currentThread().name}结束了。。。")
}
Thread_Thread-1准备好了。。。
Thread_Thread-3准备好了。。。
Thread_Thread-2准备好了。。。
Thread_Thread-0准备好了。。。
Thread_Thread-4准备好了。。。
Thread_Thread-1买了第0张票
Thread_Thread-1结束了。。。
Thread_Thread-4买了第1张票
Thread_Thread-4结束了。。。
Thread_Thread-0买了第2张票
Thread_Thread-0结束了。。。
Thread_Thread-2买了第3张票
Thread_Thread-2结束了。。。
Thread_Thread-3买了第4张票
Thread_Thread-3结束了。。。
synchronized优势,无需关心释放锁,jvm会为我们自动释放,
劣势:必须等到获取锁对象线程执行完成,或者出现异常,才能释放掉,不能中途释放,不能中断一个正在
试图获得锁的线程,另外也不知道多个线程竞争锁的时候获取锁成功与否,每个锁仅有单一的条件,不能设定超时
*/
class SynchronizedTest(){
companion object{
val ticks = mutableListOf<String>()
}
fun printTick(){
println("Thread_${Thread.currentThread().name}准备好了。。。")
synchronized(this){
Thread.sleep(1000)
println("Thread_${Thread.currentThread().name}买了${ticks.removeAt(0)}")
}
println("Thread_${Thread.currentThread().name}结束了。。。")
}
init {
for(i in 0 until 5){
ticks.add("第${i}张票")
}
}
}
fun testSynchronized(){
val synchronizedTest = SynchronizedTest()
for(i in 0 until 5){
Thread(Runnable {
synchronizedTest.printTick()
}).start()
}
}
synchronized的优势
- 哪怕我们同步方法中出现异常,jvm也能够为我们自动释放锁,能主动从而规避死锁。不需要开发者手动释放锁
劣势 - 必须要等到获取锁的对象的线程执行完成,或者出现异常,才能释放掉。不能中途释放锁,不能中断一个正在试图获得锁的进程
- 另外也不知道多个线程竞争锁的时候,获取锁成功与否,所以不够灵活
- 每个锁仅有单一的条件(某个对象)不能设定超时。
ReentrantLock 悲观锁,可重入锁,公平锁,非公平锁
- 基本用法
ReentrantLock lock = new ReentrantLock()
try{
lock.lock()
}finally{
lock.unLock()
}
void lock()//获取不到会阻塞
boolean tryLock()//尝试获取锁,成功返回true。
boolean tryLock(3000, TimeUnit.MILLISECONDS)//在一定时间内不断去尝试获取锁
void lockInterruptibly();//可使用Thread.interrupt()打断阻塞状态,退出竞争,让给其他线程
- 可重入,避免死锁
ReentrantLock lock = new ReentrantLock()
public void doWork(){
try{
lock.lock()
doWork();//递归,使得统一线程多次获得锁
}finally{
lock.unLock()
}
}
/**
* 可重入锁
* 允许同一个线程在不释放锁的情况下,再次获取锁,防止锁死
打印第一张 Thread-0
打印第二张 Thread-0
打印第三张 Thread-0
打印第四张 Thread-0
打印第五张 Thread-0
打印第一张 Thread-2
打印第二张 Thread-2
打印第三张 Thread-2
打印第四张 Thread-2
打印第五张 Thread-2
打印第一张 Thread-7
打印第二张 Thread-7
打印第三张 Thread-7
打印第四张 Thread-7
打印第五张 Thread-7
打印第一张 Thread-3
打印第二张 Thread-3
打印第三张 Thread-3
打印第四张 Thread-3
打印第五张 Thread-3
打印第一张 Thread-6
打印第二张 Thread-6
打印第三张 Thread-6
打印第四张 Thread-6
打印第五张 Thread-6
打印第一张 Thread-5
打印第二张 Thread-5
打印第三张 Thread-5
打印第四张 Thread-5
打印第五张 Thread-5
打印第一张 Thread-9
打印第二张 Thread-9
打印第三张 Thread-9
打印第四张 Thread-9
打印第五张 Thread-9
打印第一张 Thread-1
打印第二张 Thread-1
打印第三张 Thread-1
打印第四张 Thread-1
打印第五张 Thread-1
打印第一张 Thread-8
打印第二张 Thread-8
打印第三张 Thread-8
打印第四张 Thread-8
打印第五张 Thread-8
打印第一张 Thread-4
打印第二张 Thread-4
打印第三张 Thread-4
打印第四张 Thread-4
打印第五张 Thread-4
*/
class ReentrantLock2{
val lock = ReentrantLock()
fun simpleUse(){
try{
lock.lock()
println("打印第一张 ${Thread.currentThread().name}")
Thread.sleep(1000)
lock.lock()
println("打印第二张 ${Thread.currentThread().name}")
Thread.sleep(1000)
lock.lock()
println("打印第三张 ${Thread.currentThread().name}")
Thread.sleep(1000)
lock.lock()
println("打印第四张 ${Thread.currentThread().name}")
Thread.sleep(1000)
lock.lock()
println("打印第五张 ${Thread.currentThread().name}")
Thread.sleep(1000)
}finally {
/**
* 加锁几次,就要解锁几次
*/
lock.unlock()
lock.unlock()
lock.unlock()
lock.unlock()
lock.unlock()
}
}
}
/**
* 可重入锁
*/
private fun test2(){
val lockTest2 = ReentrantLockTest.ReentrantLock2()
val run = Runnable {
lockTest2.simpleUse()
}
for(i in 0 until 10){
Thread(run).start()
}
}
- 公平锁与非公平锁
- 公平锁,所有进入阻塞的线程呢排队一次均有机会执行
- 默认非公平锁,允许线程插队,避免每个线程都进入阻塞,在唤醒,性能高,因为线程可以插队,导致队列中可能存在线程饿死的情况,一直得不到锁,一直得不到执行
ReentrantLock lock = new ReentrantLock(true/false)
/**
* ReentrantLock默认为非公平锁
* 公平锁:所有进入阻塞的线程排队一次均有机会执行
* 非公平锁:允许线程插队,避免每一个线程都进入阻塞,在唤醒,性能高。因为线程可以插队,导致队列中可能会存在线程饿死的情况,一直得不到锁,一直得不到执行
* 可重入锁:允许同一个线程在不释放锁的情况下,再次获取锁,防止锁死
*/
/**
* 公平锁---非公平锁
* 使用非公平锁,在线程中释放了锁又立即获取锁,会优先执行
打印第一张 Thread-0
打印第二张 Thread-0
打印第一张 Thread-4
打印第二张 Thread-4
打印第一张 Thread-9
打印第二张 Thread-9
打印第一张 Thread-8
打印第二张 Thread-8
打印第一张 Thread-7
打印第二张 Thread-7
打印第一张 Thread-2
打印第二张 Thread-2
打印第一张 Thread-5
打印第二张 Thread-5
打印第一张 Thread-1
打印第二张 Thread-1
打印第一张 Thread-6
打印第二张 Thread-6
打印第一张 Thread-3
打印第二张 Thread-3
**
* 使用公平锁,每个线程都有相同的机会获取锁,线程一旦释放了锁,那么就会进入等待,将锁给予其他线程
打印第一张 Thread-1
打印第一张 Thread-9
打印第一张 Thread-4
打印第一张 Thread-3
打印第一张 Thread-5
打印第一张 Thread-6
打印第一张 Thread-0
打印第一张 Thread-7
打印第一张 Thread-8
打印第一张 Thread-2
打印第二张 Thread-1
打印第二张 Thread-9
打印第二张 Thread-4
打印第二张 Thread-3
打印第二张 Thread-5
打印第二张 Thread-6
打印第二张 Thread-0
打印第二张 Thread-7
打印第二张 Thread-8
打印第二张 Thread-2
*/
class ReentrantLock1{
val lock = ReentrantLock(true)
fun simpleUse(){
try{
lock.lock()
println("打印第一张 ${Thread.currentThread().name}")
Thread.sleep(1000)
lock.unlock()
lock.lock()
println("打印第二张 ${Thread.currentThread().name}")
}finally {
lock.unlock()
}
}
}
/**
* 公平锁,非公平锁
*/
private fun testSimpleUns(){
val lockTest1 = ReentrantLockTest.ReentrantLock1()
val run = Runnable {
lockTest1.simpleUse()
}
for(i in 0 until 10){
Thread(run).start()
}
}
- Condition条件唤醒
- 可使用它的awit-singnal指定唤醒一个线程。相比于wait-notify要么全部唤醒,要么只能唤醒一个,更加灵活可控。
ReentrantLock lock = new ReentrantLock()
Condition worker1 = lock.newCondition();
Condition worker2 = lock.newCondition();
class Worker1{
worker1.awit()//进入阻塞,等待唤醒
}
class Worker2{
worker2.awit()//进入阻塞,等待唤醒
}
class Boss{
if(...){
worker1.signal()//指定唤醒线程1
}else{
worker2.signal()//指定唤醒线程2
}
}
/**
* 使用Condition条件对象,精细控制ReentrantLock
* 可使用它的await-singnal指定唤醒一个线程,相比wait-notify要么全部唤醒,要么智能唤醒一个,更加灵活
* 案例,生产者线程产生数据,偶数唤醒线程1,基数唤醒线程2
* await会释放锁,所以生产者线程中也需要加入Condition
*
没砖了,work1开始休息了
没砖了,work2开始休息了
生产出来了workNumber44,唤醒work2
work2开始搬砖: workNumber:44
work2释放锁
没砖了,work2开始休息了
生产出来了workNumber98,唤醒work2
work2开始搬砖: workNumber:98
work2释放锁
没砖了,work2开始休息了
生产出来了workNumber63,唤醒work1
work1开始搬砖: workNumber:63
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber13,唤醒work1
work1开始搬砖: workNumber:13
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber3,唤醒work1
work1开始搬砖: workNumber:3
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber9,唤醒work1
work1开始搬砖: workNumber:9
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber73,唤醒work1
work1开始搬砖: workNumber:73
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber27,唤醒work1
work1开始搬砖: workNumber:27
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber97,唤醒work1
work1开始搬砖: workNumber:97
work1释放锁
没砖了,work1开始休息了
生产出来了workNumber63,唤醒work1
work1开始搬砖: workNumber:63
work1释放锁
没砖了,work1开始休息了
*/
class ReentrantLock3{
@Volatile
var workNumber = 0
val lock = ReentrantLock(false)
val condition1 = lock.newCondition()
val condition2 = lock.newCondition()
val condition3 = lock.newCondition()
fun work1(){
try {
lock.lock()
if(workNumber == 0 || workNumber % 2 == 0) {
println("没砖了,work1开始休息了")
condition1.await()
condition3.signal()
}
println("work1开始搬砖: workNumber:${workNumber} ")
workNumber = 0
}finally {
println("work1释放锁")
lock.unlock()
}
}
fun work2(){
try {
lock.lock()
if(workNumber == 0 || workNumber % 2 != 0){
println("没砖了,work2开始休息了")
condition2.await()
condition3.signal()
}
println("work2开始搬砖: workNumber:${workNumber} ")
workNumber = 0
}finally {
println("work2释放锁")
lock.unlock()
}
}
fun boos(){
try {
lock.lock()
workNumber = (0..100).random()
if(workNumber % 2 == 0){
condition2.signal()
println("生产出来了workNumber${workNumber},唤醒work2")
}else{
condition1.signal()
println("生产出来了workNumber${workNumber},唤醒work1")
}
condition3.await()
}finally {
lock.unlock()
}
}
}
/**
* Condition条件唤醒
*/
private fun test3(){
val count = AtomicInteger()
val lockTest3 = ReentrantLockTest.ReentrantLock3()
Thread(Runnable {
count.getAndIncrement()
while(true){
lockTest3.work1()
}
}).start()
Thread(Runnable {
count.getAndIncrement()
while (true){
lockTest3.work2()
}
}).start()
Thread.sleep(1000)
for(i in 0 until 10){
lockTest3.boos()
}
}
- ReentrantReadWriteLock 共享锁,排他说
- 共享锁,所有线程均可同时获得,并发量高,比如在线查看文档
- 排他锁,同一时刻只有一个线程有权修改资源,比如在线文档编辑
ReentrantReadWriteLock reentrantReadWriteLock
ReentrantReadWriteLock.ReadLock readLock
ReentrantReadWriteLock.WriteLock writeLock
/**
* 共享锁,所有线程均可同时获得,并发量高,比如在线文档查看
* 排他锁,同一时刻只有一个线程有权修改资源,比如在线文档编辑
Thread Thread-2 开始写
Thread Thread-2 释放写锁
Thread Thread-6 开始写
Thread Thread-6 释放写锁
Thread Thread-3 开始写
Thread Thread-3 释放写锁
Thread Thread-7 开始写
Thread Thread-7 释放写锁
Thread Thread-1 开始写
Thread Thread-1 释放写锁
Thread Thread-4 开始写
Thread Thread-4 释放写锁
Thread Thread-9 开始写
Thread Thread-9 释放写锁
Thread Thread-0 开始写
Thread Thread-0 释放写锁
Thread Thread-8 开始写
Thread Thread-8 释放写锁
Thread Thread-5 开始写
Thread Thread-5 释放写锁
Thread Thread-2 开始阅读
Thread Thread-4 开始阅读
Thread Thread-6 开始阅读
Thread Thread-0 开始阅读
Thread Thread-5 开始阅读
Thread Thread-1 开始阅读
Thread Thread-7 开始阅读
Thread Thread-3 开始阅读
Thread Thread-9 开始阅读
Thread Thread-8 开始阅读
Thread Thread-5 释放读锁
Thread Thread-6 释放读锁
Thread Thread-2 释放读锁
Thread Thread-0 释放读锁
Thread Thread-1 释放读锁
Thread Thread-9 释放读锁
Thread Thread-7 释放读锁
Thread Thread-3 释放读锁
Thread Thread-8 释放读锁
Thread Thread-4 释放读锁
*/
class ReentrantReadWriteLockTest{
val reentrantReadWriteLock = ReentrantReadWriteLock()
val reentrantReadLock = reentrantReadWriteLock.readLock()
val reentrantWriteLock = reentrantReadWriteLock.writeLock()
fun readWork(){
try{
reentrantReadLock.lock()
println("Thread ${Thread.currentThread().name} 开始阅读")
Thread.sleep(1000)
}finally {
println("Thread ${Thread.currentThread().name} 释放读锁")
reentrantReadLock.unlock()
}
}
fun writeWork(){
try{
reentrantWriteLock.lock()
println("Thread ${Thread.currentThread().name} 开始写")
Thread.sleep(1000)
}finally {
println("Thread ${Thread.currentThread().name} 释放写锁")
reentrantWriteLock.unlock()
}
}
}
/**
* 排他锁
* 共享锁
*/
private fun test4(){
val reentrantReadWriteLockTest = ReentrantLockTest.ReentrantReadWriteLockTest()
for(i in 0 until 10){
Thread(Runnable {
reentrantReadWriteLockTest.readWork()
}).start()
}
// for(i in 0 until 10){
// Thread(Runnable {
// reentrantReadWriteLockTest.writeWork()
// }).start()
// }
}
如何正确使用锁&原子类
- 减少持锁时间
尽管锁在同一时间只能允许一个线程持有,其他想要占用锁的线程都在临界区外等待锁释放,这个等待的时间根据实际的应用及代码写法可长可短
public void syncMethod(){
noneLockedCode();//2s
synchronized(this){
needLockedMethed();//2s
}
noneLockedCode2;//2s
}
- 锁分离 读读,读写,写读,写写。只要有些锁进入才需要做同步处理,但是对于大多数应用,读的场景远大于写。因此使用读写锁,在读多写少的场景中,就可以很好的提高系统的性能。这就是锁分离
- 锁粗化 多次加锁
public void doSomethingMethod(){
synchronized(lock){
//do some thing
}
...
//这里还有一些代码,做其他不需要同步的工作,单能很快执行完成
...
synchronized(lock){
//do other thing
}
}
//粗化
public void doSomethingMethod(){
synchronized(lock){
//do some thing
...
//这里还有一些代码,做其他不需要同步的工作,单能很快执行完成
...
//do other thing
}
}
其他控制
CountDownLatch
/**
Thread 开始运行Thread-3
Thread 开始运行Thread-4
Thread 开始运行Thread-2
Thread 开始运行Thread-1
Thread 开始运行Thread-0
Thread 结束运行Thread-2
Thread 结束运行Thread-3
Thread 结束运行Thread-4
Thread 结束运行Thread-0
Thread 结束运行Thread-1
所有线程运行结束
CountDownLatch创建时需要指定一个int值,调用await的线程将被阻塞,直到其他线程调用countDown()方法将指定int值减为0时才能回复运行
*/
private fun countDownLatchTest(){
val countDownLatch = CountDownLatch(5)
for(i in 0 until 5){
Thread(Runnable {
println("Thread 开始运行${Thread.currentThread().name}")
Thread.sleep((2000 .. 6000).random().toLong())
println("Thread 结束运行${Thread.currentThread().name}")
countDownLatch.countDown()
}).start()
}
countDownLatch.await()
println("所有线程运行结束").run { }
countDownLatch.let { }
}
Semaphore
/**
Thread 开始运行Thread-1
Thread 开始运行Thread-0
Thread 开始运行Thread-5
Thread 结束运行Thread-0
Thread 开始运行Thread-2
Thread 结束运行Thread-1
Thread 开始运行Thread-3
Thread 结束运行Thread-5
Thread 开始运行Thread-7
Thread 结束运行Thread-3
Thread 开始运行Thread-9
Thread 结束运行Thread-7
Thread 开始运行Thread-6
Thread 结束运行Thread-2
Thread 开始运行Thread-8
Thread 结束运行Thread-8
Thread 开始运行Thread-4
Thread 结束运行Thread-6
Thread 结束运行Thread-9
Thread 结束运行Thread-4
Semaphore类似与令牌,创建时需指定令牌数量和是否时公平的,当一个线程使用semaphore.acquire()获取到令牌后可以继续执行,
当令牌被获取完后需要等待其他线程释放令牌才能运行
*/
private fun semaphoreTest(){
val semaphore = Semaphore(3, true)
for(i in 0 until 10){
Thread(Runnable {
semaphore.acquire()
println("Thread 开始运行${Thread.currentThread().name}")
Thread.sleep((2000 .. 6000).random().toLong())
println("Thread 结束运行${Thread.currentThread().name}")
semaphore.release()
}).start()
}
}