多线程与并发(四):ReentrantLock

2021-09-07  本文已影响0人  lilykeke

1. 一个例子

一间大屋子有两个功能:睡觉 学习 ,互不干涉
现在小男要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低。解决方法是准备多个房间(多个对像锁)

public class Test1 {

    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        
        new Thread(()->{
            bigRoom.study();
        },"小男").start();

        new Thread(()->{
            bigRoom.sleep();
        },"小女").start();
    }
}

class BigRoom{
    public void study(){
        synchronized (this){
            System.out.println("学习一小时");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void sleep(){
        synchronized (this){
            System.out.println("睡觉一小时");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

改进

public class Test1 {

    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();

        new Thread(()->{
            bigRoom.study();
        },"小男").start();

        new Thread(()->{
            bigRoom.sleep();
        },"小女").start();
    }


}

class BigRoom{
    
    private final Object studyRoom = new Object();
    private final Object sleepRoom = new Object();
    public void study(){
        synchronized (studyRoom){
            System.out.println("学习一小时");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void sleep(){
        synchronized (sleepRoom){
            System.out.println("睡觉一小时");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

将锁的粒度细分

1.1 活跃性

死锁

有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁

t1 线程获得 A对象锁,接下来想获取B对象锁
t2 线程获得B对象锁,接下来想获取A对象锁

例如:

public class Test2 {

    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();

        Thread t1 = new Thread(()->{
            synchronized (A){
                System.out.println(Thread.currentThread().getName()+"lock A");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B){
                    System.out.println(Thread.currentThread().getName()+"lock B");
                }
            }
        },"t1");

        Thread t2 = new Thread(()->{
            synchronized (B){
                System.out.println(Thread.currentThread().getName()+"lock B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A){
                    System.out.println(Thread.currentThread().getName()+"lock A");
                }
            }
        },"t2");

        t1.start();
        t2.start();
    }
}

结果:
t1lock A
t2lock B


定位死锁

ItsBadForYadeMacBook-Pro:lily itsbadforya$ jps
26273 Launcher
26275 Test2
25431 Test
25194 
26285 Jps
26270 KotlinCompileDaemon
ItsBadForYadeMacBook-Pro:lily itsbadforya$ jstack 26275
2020-10-05 14:39:51
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"Attach Listener" #16 daemon prio=9 os_prio=31 tid=0x00007ff49e827000 nid=0x470b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #15 prio=5 os_prio=31 tid=0x00007ff4a1159800 nid=0xf03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #14 prio=5 os_prio=31 tid=0x00007ff4a08d6800 nid=0xa403 waiting for monitor entry [0x0000700006a75000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Test2.lambda$main$1(Test2.java:32)
        - waiting to lock <0x00000007957dab48> (a java.lang.Object)
        - locked <0x00000007957dab58> (a java.lang.Object)
        at Thread.Test2$$Lambda$2/1879492184.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"t1" #13 prio=5 os_prio=31 tid=0x00007ff4a0846000 nid=0x5703 waiting for monitor entry [0x0000700006972000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Test2.lambda$main$0(Test2.java:18)
        - waiting to lock <0x00000007957dab58> (a java.lang.Object)
        - locked <0x00000007957dab48> (a java.lang.Object)
        at Thread.Test2$$Lambda$1/1674896058.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #12 daemon prio=9 os_prio=31 tid=0x00007ff49e03f000 nid=0xa803 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #11 daemon prio=9 os_prio=31 tid=0x00007ff4a086f000 nid=0x5503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007ff4a086e000 nid=0x3c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007ff4a086d000 nid=0x3d03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007ff4a086c800 nid=0x3903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007ff4a380b800 nid=0x3803 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007ff49e817800 nid=0x4003 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007ff49e816800 nid=0x3607 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007ff49e810000 nid=0x3503 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007ff4a3808800 nid=0x2f03 in Object.wait() [0x0000700005d4b000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007ff4a0826800 nid=0x4d03 in Object.wait() [0x0000700005c48000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007ff4a081f800 nid=0x4e03 runnable 

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007ff49e00d000 nid=0x2307 runnable 

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007ff49e010000 nid=0x2103 runnable 

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007ff49e010800 nid=0x1e03 runnable 

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007ff49e011800 nid=0x2a03 runnable 

"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007ff49e012000 nid=0x5403 runnable 

"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007ff49e012800 nid=0x2d03 runnable 

"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007ff49e013000 nid=0x5203 runnable 

"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007ff49e014000 nid=0x5003 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007ff4a38b8000 nid=0x5603 waiting on condition 

JNI global references: 2246


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x00007ff4a082c4a8 (object 0x00000007957dab48, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x00007ff4a0828408 (object 0x00000007957dab58, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at Thread.Test2.lambda$main$1(Test2.java:32)
        - waiting to lock <0x00000007957dab48> (a java.lang.Object)
        - locked <0x00000007957dab58> (a java.lang.Object)
        at Thread.Test2$$Lambda$2/1879492184.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"t1":
        at Thread.Test2.lambda$main$0(Test2.java:18)
        - waiting to lock <0x00000007957dab58> (a java.lang.Object)
        - locked <0x00000007957dab48> (a java.lang.Object)
        at Thread.Test2$$Lambda$1/1674896058.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

ItsBadForYadeMacBook-Pro:lily itsbadforya$ 

哲学家就餐问题

有五位哲学家,围坐在圆桌旁

public class Test3 {


    public static void main(String[] args) {
        //创建五根筷子

        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        //创建五个哲学家
        new Philosopher("苏格拉底",c1,c2).start();
        new Philosopher("柏拉图",c2,c3).start();
        new Philosopher("亚里士多德",c3,c4).start();
        new Philosopher("赫拉克利特",c4,c5).start();
        new Philosopher("阿基米德",c5,c1).start();

    }

}

class Philosopher extends Thread{
    Chopstick left;
    Chopstick right;


    public Philosopher(String name, Chopstick left,Chopstick right){
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while(true){
            //尝试获得左手的筷子
            synchronized (left){
                //尝试获得右手的筷子
                synchronized (right){
                    eat();
                }
            }
        }
    }


    private void eat(){
        System.out.println(Thread.currentThread().getName()+"eating...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class Chopstick{
    String name;


    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}

执行一段时间后出现死锁,问题定位:

ItsBadForYadeMacBook-Pro:lily itsbadforya$ jstack 26610
2020-10-05 20:02:07
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"Attach Listener" #19 daemon prio=9 os_prio=31 tid=0x00007f868d027000 nid=0x3707 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #18 prio=5 os_prio=31 tid=0x00007f868d812000 nid=0x1003 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"阿基米德" #17 prio=5 os_prio=31 tid=0x00007f868c048000 nid=0xa103 waiting for monitor entry [0x00007000062fd000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc120> (a Thread.Chopstick)
        - locked <0x00000007957dc190> (a Thread.Chopstick)

"赫拉克利特" #16 prio=5 os_prio=31 tid=0x00007f868d811000 nid=0x5a03 waiting for monitor entry [0x00007000061fa000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc190> (a Thread.Chopstick)
        - locked <0x00000007957dc180> (a Thread.Chopstick)

"亚里士多德" #15 prio=5 os_prio=31 tid=0x00007f8689839000 nid=0xa403 waiting for monitor entry [0x00007000060f7000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc180> (a Thread.Chopstick)
        - locked <0x00000007957dc170> (a Thread.Chopstick)

"柏拉图" #14 prio=5 os_prio=31 tid=0x00007f8688851000 nid=0xa503 waiting for monitor entry [0x0000700005ff4000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc170> (a Thread.Chopstick)
        - locked <0x00000007957dc160> (a Thread.Chopstick)

"苏格拉底" #13 prio=5 os_prio=31 tid=0x00007f8688850800 nid=0xa603 waiting for monitor entry [0x0000700005ef1000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc160> (a Thread.Chopstick)
        - locked <0x00000007957dc120> (a Thread.Chopstick)

"Service Thread" #12 daemon prio=9 os_prio=31 tid=0x00007f868784d000 nid=0xa903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #11 daemon prio=9 os_prio=31 tid=0x00007f868d808800 nid=0x5503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007f8688852000 nid=0x4103 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007f868884f800 nid=0x3f03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007f868884f000 nid=0x3d03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007f8689833000 nid=0x3c03 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007f868d008800 nid=0x4503 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007f8687808800 nid=0x4707 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007f868801a000 nid=0x4803 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007f8689830800 nid=0x4f03 in Object.wait() [0x00007000052ca000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007f8688812800 nid=0x5103 in Object.wait() [0x00007000051c7000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007f868982f800 nid=0x5303 runnable 

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007f868800b800 nid=0x2307 runnable 

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007f868800f000 nid=0x2103 runnable 

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007f8688010000 nid=0x1e03 runnable 

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007f868880a800 nid=0x2a03 runnable 

"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007f8688010800 nid=0x2c03 runnable 

"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007f868880b800 nid=0x2e03 runnable 

"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007f8688011000 nid=0x3003 runnable 

"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007f8688011800 nid=0x5403 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007f868c00b000 nid=0x5703 waiting on condition 

JNI global references: 1441


Found one Java-level deadlock:
=============================
"阿基米德":
  waiting to lock monitor 0x00007f86888540b8 (object 0x00000007957dc120, a Thread.Chopstick),
  which is held by "苏格拉底"
"苏格拉底":
  waiting to lock monitor 0x00007f86880177f8 (object 0x00000007957dc160, a Thread.Chopstick),
  which is held by "柏拉图"
"柏拉图":
  waiting to lock monitor 0x00007f8688017748 (object 0x00000007957dc170, a Thread.Chopstick),
  which is held by "亚里士多德"
"亚里士多德":
  waiting to lock monitor 0x00007f8688854008 (object 0x00000007957dc180, a Thread.Chopstick),
  which is held by "赫拉克利特"
"赫拉克利特":
  waiting to lock monitor 0x00007f8688853f58 (object 0x00000007957dc190, a Thread.Chopstick),
  which is held by "阿基米德"

Java stack information for the threads listed above:
===================================================
"阿基米德":
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc120> (a Thread.Chopstick)
        - locked <0x00000007957dc190> (a Thread.Chopstick)
"苏格拉底":
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc160> (a Thread.Chopstick)
        - locked <0x00000007957dc120> (a Thread.Chopstick)
"柏拉图":
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc170> (a Thread.Chopstick)
        - locked <0x00000007957dc160> (a Thread.Chopstick)
"亚里士多德":
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc180> (a Thread.Chopstick)
        - locked <0x00000007957dc170> (a Thread.Chopstick)
"赫拉克利特":
        at Thread.Philosopher.run(Test3.java:44)
        - waiting to lock <0x00000007957dc190> (a Thread.Chopstick)
        - locked <0x00000007957dc180> (a Thread.Chopstick)

Found 1 deadlock.

活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如:

public class Test4 {
    static volatile int count = 10;
    static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            //期望减到0退出循环

            while (count > 0) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                count--;
                System.out.println(Thread.currentThread().getName() + "current count is " + count);
            }
        }, "t1").start();

        new Thread(() -> {
            //期望超过20退出循环

            while (count < 20) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                count++;
                System.out.println(Thread.currentThread().getName() + "current count is " + count);
            }
        }, "t2").start();
    }
}

执行结果:不能停止
解决方法:睡眠的时间要不一样

饥饿

很多线程中把饥饿定义为,一个线程由于优先级太低,始终得不到CPU调度,也不能够结束,饥饿的情况不易演示,这样读写锁时会涉及饥饿的问题。

下面我讲一个我遇到的线程饥饿的例子,先来看看使用顺序加锁的方式解决之前死锁的问题


死锁.jpg

顺序加锁的解决方案


死锁-2.jpg

没有死锁,但是有可能会造成线程饥饿,得不到执行

2. ReentrantLock

相对于synchronized 它具备如下特点:

//获取锁
reentrantLock.lock();
try{
    //临界区
}finally{
    //释放锁
    reentrantLock.unlock();
}

2.1 可重入

可重入是指同一个线程如果首次获得这把锁,那么因为它是这把锁的拥有者,因此具有权利再次获取这把锁。如果不是可重入锁,那么第二次获得锁的时候,自己会被锁挡住。

public class Test5 {

    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        method1();
    }

    public static void method1(){
        lock.lock();

        try {
            System.out.println("excute method1");
            method2();
        } finally {
            lock.unlock();
        }
    }

    public static void method2(){
        lock.lock();

        try {
            System.out.println("excute method2");
            method3();
        } finally {
            lock.unlock();
        }
    }

    public static void method3(){
        lock.lock();

        try {
            System.out.println("excute method3");

        } finally {
            lock.unlock();
        }
    }
}

2.2 可打断

public class Test5 {

    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread t1 = new Thread(()->{
            try {
                //如果没有竞争那么此方法就会获取lock对象锁
                //如果有竞争就进入阻塞队列,可以被其它线程用interrupt()方法打断
                System.out.println("尝试获得锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("没有获得锁,返回");
                return;
            }

            try {
                System.out.println("获取到锁");
            } finally {
                lock.unlock();
            }
        },"t1");

        lock.lock();

        t1.start();


        try {
            Thread.sleep(1000);
            System.out.println("打断t1");
            t1.interrupt();
        } catch(InterruptedException e) {

        }

    }

执行结果:

尝试获得锁
打断t1
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    at Thread.Test5.lambda$main$0(Test5.java:16)
    at java.lang.Thread.run(Thread.java:748)
没有获得锁,返回

2.3 锁超时

立刻失败

public class Thread5 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            System.out.println("启动");
            System.out.println("尝试获取锁");
            if (!lock.tryLock()) {
                System.out.println("获取立刻失败,返回");
                return;
            }

            try {
                System.out.println(Thread.currentThread().getName() + "获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获得了锁");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

执行结果:
main获得了锁
启动
尝试获取锁
获取立刻失败,返回


带参数的tryLock(long n,TimeUnit unit)

public class Thread5 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            System.out.println("启动");
            System.out.println("尝试获取锁");
            try {
                if (!lock.tryLock(2, TimeUnit.SECONDS)) {
                    System.out.println("获取立刻失败,返回");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                System.out.println(Thread.currentThread().getName() + "获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获得了锁");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

执行结果:
main获得了锁
启动
尝试获取锁
t1获得了锁


2.4 锁超时-解决哲学家就餐问题

public class Test3 {


    public static void main(String[] args) {
        //创建五根筷子

        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        //创建五个哲学家
        new Philosopher("苏格拉底", c1, c2).start();
        new Philosopher("柏拉图", c2, c3).start();
        new Philosopher("亚里士多德", c3, c4).start();
        new Philosopher("赫拉克利特", c4, c5).start();
        new Philosopher("阿基米德", c5, c1).start();

    }

}

class Philosopher extends Thread {
    Chopstick left;
    Chopstick right;


    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            //尝试获得左手的筷子
            if (left.tryLock()) {
                try{
                    //尝试获得右手的筷子

                    if (right.tryLock()){
                        try{
                            eat();
                        }finally {
                            right.unlock();
                        }
                    }
                }finally {
                    left.unlock();//获取右边筷子失败,释放自己的左手筷子
                }
            }

        }
    }


    private void eat() {
        System.out.println(Thread.currentThread().getName() + "eating...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class Chopstick extends ReentrantLock {
    String name;


    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}

2.5 公平锁

ReentrantLock默认是不公平的

公平锁一般没有必要,会降低并发度。

2.6 条件变量

synchronized 中也有条件变量,就是我们讲原理的那个waitSet休息室,当条件不满足时进入waitSet等待。
ReentrantLock的条件变量比synchronized 强大之处在于,它是支持多个条件变量的,这就好比

使用流程

public class Thread6 {
    
    static ReentrantLock Room = new ReentrantLock();
    //创建条件变量
    static Condition waitCigaretteQueue = Room.newCondition();
    static Condition waitBreakfastQueue = Room.newCondition();

    static volatile boolean hasCigarette;
    static volatile boolean hasBreakfast;

    public static void main(String[] args) {

        new Thread(() -> {

            Room.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "有烟没?" + hasCigarette);
                while (!hasCigarette) {
                    try {
                        waitCigaretteQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName() + "再次问有烟没?" + hasCigarette);
                if (hasCigarette) {
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                } else {
                    System.out.println("没干成");
                }
            } finally {
                Room.unlock();
            }

        }, "小男").start();


        new Thread(() -> {

            Room.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "有早餐没?" + hasBreakfast);
                while (!hasBreakfast) {
                    try {
                        waitBreakfastQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName() + "再次问有早餐没?" + hasBreakfast);
                if (hasBreakfast) {
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                } else {
                    System.out.println("没干成");
                }
            } finally {
                Room.unlock();
            }

        }, "小女").start();

        new Thread(() -> {
            Room.lock();

            try {
                System.out.println("送外卖来了");
                hasBreakfast = true;
                waitBreakfastQueue.signal();
            } finally {
                Room.unlock();
            }
        }, "送外卖的").start();

        new Thread(() -> {
            Room.lock();

            try {
                System.out.println("送烟来了");
                hasCigarette = true;
                waitCigaretteQueue.signal();
            } finally {
                Room.unlock();
            }
        }, "送烟的").start();
    }
}

执行结果:
小男有烟没?false
小女有早餐没?false
送外卖来了
小女再次问有早餐没?true
小女可以开始干活了
送烟来了
小男再次问有烟没?true
小男可以开始干活了


同步模式之顺序控制

比如,必须先2后1打印
wait-notify

public class Test6 {

    static Object obj = new Object();
    //t2运行标记,代表t2是否执行过
    static boolean t2runed = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            synchronized (obj){
                while (!t2runed){
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("打印1");
            }

        },"t1");

        Thread t2 = new Thread(()->{
            synchronized (obj){
                System.out.println("打印2");
                t2runed = true;
                obj.notify();
            }
        },"t2");

        t1.start();
        t2.start();
    }
}

执行结果:
打印2
打印1


park & unpark解决方式
public class Test7 {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {

            LockSupport.park();
            System.out.println("打印1");

        }, "t1");

        Thread t2 = new Thread(() -> {

            System.out.println("打印2");
            LockSupport.unpark(t1);

        }, "t2");

        t1.start();
        t2.start();
    }
}

执行结果:
打印2
打印1


交替输出

线程1 输出a5次,线程2输出b 5次,线程3输出c 5次。要求输出abcabcabcabcabc

wait - notify版本
public class Test8 {

    public static void main(String[] args) {
        WaitNotify waitNotify = new WaitNotify(1,5);
        new Thread(()->{
            waitNotify.print("a",1,2);
        }).start();
        new Thread(()->{
            waitNotify.print("b",2,3);
        }).start();
        new Thread(()->{
            waitNotify.print("c",3,1);
        }).start();
    }
}

class WaitNotify{
    //等待标记
    private int flags;
    //循环次数
    private int loopNumber;

    public WaitNotify(int flags, int loopNumber){
        this.flags = flags;
        this.loopNumber = loopNumber;
    }

    public void print(String str, int waitFlags, int nextFlags){
        for (int i = 0; i < loopNumber; i++) {
            synchronized (this){
                while (flags!= waitFlags){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.print(str);
                flags = nextFlags;
                this.notifyAll();
            }
        }

    }
}

ReentrantLock 方式

public class Test9 {
    public static void main(String[] args) {
        AwaitSignal awaitSignal = new AwaitSignal(5);
        Condition a = awaitSignal.newCondition();
        Condition b = awaitSignal.newCondition();
        Condition c = awaitSignal.newCondition();

        new Thread(() -> {
            awaitSignal.print("a", a, b);
        }).start();
        new Thread(() -> {
            awaitSignal.print("b", b, c);
        }).start();
        new Thread(() -> {
            awaitSignal.print("c", c, a);
        }).start();

        //所有的线程先进入休息室,然后由主线程唤醒

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        awaitSignal.lock();
        try{
            a.signal();
        }finally {
            awaitSignal.unlock();
        }

    }
}


class AwaitSignal extends ReentrantLock {
    private int loopNumber;

    public AwaitSignal(int i) {
        loopNumber = i;
    }

    //参数一:打印内容,参数二:进入哪一间休息室等待,参数三:下一间休息室
    public void print(String str, Condition current, Condition next) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {
                current.await();
                System.out.print(str);
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }
        }
    }
}

park&unpark方式

public class Test10 {

    private static Thread t1, t2, t3;

    public static void main(String[] args) {
        ParkUnpark parkUnpark = new ParkUnpark(5);
        t1 = new Thread(() -> {
            parkUnpark.print("a", t2);
        });
        t2 = new Thread(() -> {
            parkUnpark.print("b", t3);
        });
        t3 = new Thread(() -> {
            parkUnpark.print("c", t1);
        });

        t1.start();
        t2.start();
        t3.start();

        LockSupport.unpark(t1);
    }

}

class ParkUnpark {

    private int loopNumber;

    public ParkUnpark(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(String str, Thread next) {
        for (int i = 0; i < loopNumber; i++) {
            LockSupport.park();
            System.out.print(str);

            LockSupport.unpark(next);
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读