Android常用多线程解析(二)线程安全

如遇图片无法加载请点击此链接

线程的优先级

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)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/519111dd6e104a16a131dbdecb96653e~tplv-k3u1fbpfcp-watermark.image?)]

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)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b038ee04fb7149edaee3f0b756a21408~tplv-k3u1fbpfcp-watermark.image?)]
其实主要就是 Synchronized 和 ReentrantLock

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1dWolq28-1665970531027)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/22abf7fa4ad04b10928a93de0c764e10~tplv-k3u1fbpfcp-watermark.image?)]

Atomic

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOTXD9OE-1665970531028)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9559832b611248778291adf77a3eb38f~tplv-k3u1fbpfcp-watermark.image?)]

原子类和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()
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值