欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Lock的使用

程序员文章站 2024-01-09 19:29:46
...

一、Lock的使用

1.1 ReentrantLock类

可以实现下线程间的同步互斥,类似于Synchronized的功能,且有更加强大的嗅探锁定,多路分支通知等功能。

1.2 使用ReentrantLock实现同步

  • lock()方法获得锁
  • unlock()方法释放锁

测试类:

public class RenntrankLock_test {
    Lock lock = new ReentrantLock();
    public void doSth(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " " + "你好");
            Thread.sleep(4000);
            System.out.println(Thread.currentThread().getName() + " " +"你好结束");
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void showSth(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " " +"hello");
            Thread.sleep(4000);
            System.out.println(Thread.currentThread().getName() + " " +"world");
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void eatSth(){
        try {
            System.out.println(Thread.currentThread().getName() + " " +"eat");
            Thread.sleep(4000);
            System.out.println(Thread.currentThread().getName() + " " +"no eat");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程A:

public class ThreadA extends Thread{
    RenntrankLock_test renntrankLock_test;
    public ThreadA(RenntrankLock_test renntrankLock_test){
        this.renntrankLock_test = renntrankLock_test;
    }

    @Override
    public void run() {
        renntrankLock_test.doSth();
    }
}

线程B:

public class ThreadB extends  Thread {
    RenntrankLock_test renntrankLock_test;
    public ThreadB(RenntrankLock_test renntrankLock_test){
        this.renntrankLock_test = renntrankLock_test;
    }

    @Override
    public void run() {
        renntrankLock_test.showSth();
    }
}

线程C:

public class ThreadC extends Thread {
    RenntrankLock_test renntrankLock_test;
    public ThreadC(RenntrankLock_test renntrankLock_test){
        this.renntrankLock_test = renntrankLock_test;
    }

    @Override
    public void run() {
        renntrankLock_test.eatSth();
    }
}

主类:

public class main {
    public static void main(String[] args) {
        RenntrankLock_test renntrankLock_test = new RenntrankLock_test();
        ThreadA threadA = new ThreadA(renntrankLock_test);
        ThreadB threadB = new ThreadB(renntrankLock_test);
        ThreadC threadC = new ThreadC(renntrankLock_test);
        threadA.setName("A");
        threadB.setName("B");
        threadC.setName("C");
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

结果:

B hello
C eat
B world
C no eat
A 你好
A 你好结束

结果说明:

先执行线程B的方法,线程B获得锁,再执行sleep方法时,不释放锁,所以执行非同步方法所在的线程C,C进入sleep,执行B剩下的,B执行结束,释放锁,C执行结束,A抢到锁,执行A到结束。

二、使用Condition实现等待/通知

2.1 ReentrantLock和Condition

关键字synchronized与wait和notifyAll/notify方法结合可以实现等待/通知模式,类ReentrantLock和Condition结合也可以实现等待/通知模式

2.2 Condition类

  • JDK5出现的,使用它有更好的灵活性,比如可以实现多路通知功能,也就是可以在一个Lock对象里创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,在调度线程上更加灵活。
  • 在使用notify/notifyAll()方法时,被通知的线程是jvm随机选择的。但是Condition是可以选择性通知的。会节省一部分的效率。
  • 在使用condition.await()方法之前必须调用lock.lock()方法获得同步监视器
  • 获得Lock实例
private Lock lock = new ReentrantLock();
  • 获得Condition实例
private Condition condition = lock.newCondition();

2.3 ReentrantLock和Condition实现简单等待/通知

  • 测试类:
public class waitNotify {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void await(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "准备进行等待");
            condition.await();
            System.out.println(Thread.currentThread().getName() + "结束等待");
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void signalAll(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "准备通知");
        condition.signal();
        System.out.println(Thread.currentThread().getName() +  "通知完后在再释放锁");
        System.out.println(Thread.currentThread().getName() + "通知结束");
        lock.unlock();
    }
}

等待线程:

public class awaitThread extends Thread {
    waitNotify waitNotify;
    public awaitThread(waitNotify waitNotify){
        this.waitNotify = waitNotify;
    }

    @Override
    public void run() {
        waitNotify.await();
    }
}

通知线程:

public class signalThread extends Thread{
    waitNotify waitNotify;
    public signalThread(waitNotify waitNotify){
        this.waitNotify = waitNotify;
    }

    @Override
    public void run() {
        waitNotify.signalAll();
    }
}

主类:

public class mainTest1 {
    public static void main(String[] args) {
        try {
            waitNotify waitNotify = new waitNotify();
            new awaitThread(waitNotify).start();
            Thread.sleep(1000);
            new signalThread(waitNotify).start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

Thread-0准备进行等待
Thread-1准备通知
Thread-1通知完后在再释放锁
Thread-1通知结束
Thread-0结束等待

2.3 多个Condition实现通知部分线程

  • signalAll()
    唤醒所有线程

测试:

public class waitNotify {
    Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            //System.out.println(Thread.currentThread().getName() + "conditionA准备进行等待");
            System.out.println("A在等待");
            conditionA.await();
            System.out.println("A被唤醒");
            //System.out.println(Thread.currentThread().getName() + "conditionA结束等待");
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void signalAllA(){
        lock.lock();
        //System.out.println(Thread.currentThread().getName() + "conditionA准备通知");
        conditionA.signalAll();
        System.out.println("执行A的signalAll方法");
        //System.out.println(Thread.currentThread().getName() +  "conditionA通知完后在再释放锁");
        //System.out.println(Thread.currentThread().getName() + "conditionA通知结束");
        lock.unlock();
    }
    public void awaitB(){
        try {
            lock.lock();
            //System.out.println(Thread.currentThread().getName() + "conditionB准备进行等待");
            System.out.println("B在等待");
            conditionB.await();
            System.out.println("B被唤醒");
            System.out.println(Thread.currentThread().getName() + "conditionB结束等待");
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void signalAllB(){
        lock.lock();
        //System.out.println(Thread.currentThread().getName() + "conditionB准备通知");
        conditionB.signalAll();
        System.out.println("执行B的signalAll方法");
        //System.out.println(Thread.currentThread().getName() +  "conditionB通知完后在再释放锁");
        //System.out.println(Thread.currentThread().getName() + "conditionB通知结束");
        lock.unlock();
    }
}

wait线程:

public class awaitThread extends Thread {
    waitNotify waitNotify;
    public awaitThread(waitNotify waitNotify){
        this.waitNotify = waitNotify;
    }

    @Override
    public void run() {
        try {
            waitNotify.awaitA();
            Thread.sleep(4000);
            waitNotify.awaitB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

signal线程:

public class signalThread extends Thread{
    waitNotify waitNotify;
    public signalThread(waitNotify waitNotify){
        this.waitNotify = waitNotify;
    }

    @Override
    public void run() {
        waitNotify.signalAllA();
    }
}

主方法:

public class mainTest1 {
    public static void main(String[] args) {
        try {
            waitNotify waitNotify = new waitNotify();
            new awaitThread(waitNotify).start();
            Thread.sleep(1000);
            new signalThread(waitNotify).start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

A在等待
执行A的signalAll方法
A被唤醒
B在等待
.........

2.4 实现生产者一对一

测试类:

public class produceCost {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    boolean flag;

    public void showA() {
        try {
            lock.lock();
            while (flag) {
                condition.await();
                System.out.println(Thread.currentThread().getName() + "   " + "*****");
            }
            flag = true;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void showB() {
        try {
            lock.lock();
            while (!flag) {
                condition.await();
                System.out.println(Thread.currentThread().getName() + "   " + "-----");
            }
            flag = false;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

线程A:

public class A extends Thread {
    produceCost produceCost;
    public A(produceCost produceCost){
        this.produceCost = produceCost;
    }

    @Override
    public void run() {
        while (true) {
            produceCost.showA();
        }
    }
}

线程B:

public class B extends Thread {
    produceCost produceCost;
    public B(produceCost produceCost){
        this.produceCost = produceCost;
    }

    @Override
    public void run() {
        while (true) {
            produceCost.showB();
        }
    }
}

主类:

public class test2 {
    public static void main(String[] args) {
        produceCost produceCost = new produceCost();
        int j = 0;
            A a = new A(produceCost);
            a.setName("A");
            a.start();
            B b = new B(produceCost);
            b.setName("B");
            b.start();
    }
}

结果:

A   *****
B   -----
A   *****
B   -----
A   *****
B   -----
........

说明:

生产者和消费者各有一组通知/等待方法

2.5 生产者/消费者:多对多交替打印

测试类:

public class produceCost {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    boolean flag;

    public void showA() {
        try {
            lock.lock();
            while (flag) {
                condition.await();
            }
            flag = true;
            System.out.println(Thread.currentThread().getName() + "   " + "*****");
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void showB() {
        try {
            lock.lock();
            while (!flag) {
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "   " + "-----");
            flag = false;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

主类:

public class test2 {
    public static void main(String[] args) {
        produceCost produceCost = new produceCost();
        int j = 0;
        for (int i = 0; i < 10; i++) {
            A a = new A(produceCost);
            B b = new B(produceCost);
            a.setName("A" +i);
            b.setName("B" +i);
            a.start();
            b.start();
        }
    }
}

结果:

A1   *****
B2   -----
A7   *****
B3   -----
A1   *****
B2   -----
A7   *****

说明:打印语句放在nofityAll()方法前执行,不能在wait方法后执行,因为notifyAll()方法会唤醒同类线程导致打印语句出错。

2.6 公平锁与非公平锁

  • 公平锁(创建Reentrantlock时参数为true)
    公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的
  • 非公平锁 (正常创建ReentrantLock即可)
    获取锁的抢占机制,即是随机获取锁的,可能造成某些线程一直拿不到锁,所以就不公平了。

测试类:

public class fairLock {
    Lock lock = new ReentrantLock(true);
    public void doSth(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获的锁子");
        lock.unlock();
    }
}

线程:

public class orignThread extends Thread {
    fairLock fairLock;
    public orignThread(fairLock fairLock){
        this.fairLock = fairLock;
    }

    @Override
    public void run() {
        fairLock.doSth();
    }
}

主类:

public class test3 {
    public static void main(String[] args) {
        fairLock fairLock = new fairLock();
        orignThread thead[] = new orignThread[10];
        for (int i = 0; i < 10; i++) {
            thead[i] = new orignThread(fairLock);
            thead[i].setName("A" + i);
        }
        for (int i = 0; i < 10; i++) {
            thead[i].start();
        }
    }
}

结果:

A0获的锁子
A1获的锁子
A2获的锁子
A7获的锁子
A3获的锁子
A4获的锁子
A5获的锁子
A6获的锁子
A9获的锁子
A8获的锁子

说明:基本上是按加锁的顺序获的锁,测得时候尽量多开几个线程,100个以上才能看出效果

三、ReentrantLock的其他方法

3.1 getHoldCount()

  • 查询当前线程保持此锁定的个数,也就是调用lock()方法的次数
public class fairLock {
    ReentrantLock lock = new ReentrantLock();
    public void doSth(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获的锁子" + lock.getHoldCount());
        lock.unlock();
    }
}

输出:

A0获的锁子1
A3获的锁子1
A1获的锁子1
A4获的锁子1
A2获的锁子1
A6获的锁子1
..........

说明:

每个线程都只获得了一次锁

3.2 int getQueueLength()

  • 返回正等待获取次锁定的个数
public class fairLock {
    ReentrantLock lock = new ReentrantLock();
    public void doSth(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() + " " + lock.getQueueLength());
        lock.unlock();
    }
}

结果:

A0 0
A50 49
A52 50
A1 50
A55 50
A2 49
.........

3.3 int getWaitQueueLength(Condition condition)

  • 返回等待与此锁定相关的给定条件Condition的线程估计数

测试:

public class fairLock {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void doSth(){
        try {
            lock.lock();
            System.out.println("正在等待的个数" + " " + lock.getWaitQueueLength(condition));
            condition.await();
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

正在等待的个数 0
正在等待的个数 1
正在等待的个数 2
正在等待的个数 3
正在等待的个数 4
正在等待的个数 5
正在等待的个数 6
正在等待的个数 7
.........

3.4 boolean hasQueuedThread(Thread thread)

  • 查询指定的线程是否正在等待获取次锁定

测试:

public class fairLock {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void doSth(){
        try {
            lock.lock();
            Thread.sleep(100000);
            condition.await();
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

主类:

public class test3 {
    public static void main(String[] args) {
        try {
            fairLock fairLock = new fairLock();
            orignThread thead[] = new orignThread[100];
            for (int i = 0; i < 2; i++) {
                thead[i] = new orignThread(fairLock);
                thead[i].setName("A" + i);
            }
            for (int i = 0; i < 2; i++) {
                thead[i].start();
            }
            Thread.sleep(1000);
            System.out.println("是否正在等待此锁定" + fairLock.lock.hasQueuedThread(thead[1]));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

是否正在等待此锁定true

3.5 boolean hasWaiters(Condition condition)

  • 查询是否有线程等待与此锁定有关的Condition条件

测试类:

public class fairLock {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void doSth(){
        try {
            lock.lock();
            condition.await();
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getWaiters(){
        lock.lock();
        System.out.println(lock.hasWaiters(condition));
        lock.unlock();
    }
}

主类:

public class test3 {
    public static void main(String[] args) {
        try {
            fairLock fairLock = new fairLock();
            orignThread thead[] = new orignThread[100];
            for (int i = 0; i < 2; i++) {
                thead[i] = new orignThread(fairLock);
                thead[i].setName("A" + i);
            }
            for (int i = 0; i < 2; i++) {
                thead[i].start();
            }
            Thread.sleep(1000);
            fairLock.getWaiters();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

true

说明:

该方法必须在同步方法中进行。

3.6 其他方法

  • isFair()
    判断锁是不是公平锁
lock.isFair()
  • isHeldByCurrentThread()
    查询当前线程是否保持此锁定
lock.isHeldByCurrentThread()
  • isLocked()
    查询此所是否由任意线程保持
lock.isLocked()
  • lockInterruptibly()
    如果当前线程未被中断,则获得此锁定,如果中断则出现异常
lock.lockInterruptibly()
  • tryLock()
    仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。

3.6 使用Condition实现顺序执行

测试类:

public class ConditionTest {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    static int i = 0;
    public void run2(){
        try {
            lock.lock();
            while ( i % 3 != 0) {
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "*****");
            i = i + 1;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void run3(){
        try {
            lock.lock();
            while ( i % 3 != 1) {
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "-----");
            i = i + 1;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void run1(){
        try {
            lock.lock();
            while ( i % 3 != 2) {
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "+++++");
            i = i + 1;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
};

线程A:

public class ThreadA extends Thread{
    ConditionTest conditionTest;
    public ThreadA(ConditionTest conditionTest){
        this.conditionTest = conditionTest;
    }
    @Override
    public void run() {
        while (true) {
            conditionTest.run1();
        }
    }
}

线程B:

public class ThreadB extends  Thread {
    ConditionTest conditionTest;
    public ThreadB(ConditionTest conditionTest){
        this.conditionTest = conditionTest;
    }
    @Override
    public void run() {
        while (true) {
            conditionTest.run2();
        }
    }
}

线程C:

public class ThreadC extends  Thread {
    ConditionTest conditionTest;
    public ThreadC(ConditionTest conditionTest){
        this.conditionTest = conditionTest;
    }
    @Override
    public void run() {
        while (true) {
            conditionTest.run3();
        }
    }
}

主方法:

public class main {
    public static void main(String[] args) {
        try {
            ConditionTest conditionTest = new ConditionTest();
            ThreadA threadA = new ThreadA(conditionTest);
            ThreadB threadB = new ThreadB(conditionTest);
            ThreadC threadC = new ThreadC(conditionTest);
            threadA.setName("A");
            threadB.setName("B");
            threadC.setName("C");
            threadA.start();
            threadB.start();
            threadC.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

..........
C-----
A+++++
B*****
C-----
A+++++
B*****
C-----
A+++++
B*****
C-----
..........

四、ReentrantReadWriteLock类

4.1 ReentrantLock类的缺点

ReentrantLock具有完全互斥排他的效果,即在同一时间只有一个线程在执行ReentrantLock.lock()方法后的任务,效率十分低下,所以提供了读写锁类ReentrantReadWriteLock。

4.2 ReentrantReadWriteLock类的锁

有两个锁,一个是读操作相关的锁,也称为共享锁,一个是写操作相关的锁,也叫排它锁,也就是多个读锁之间不互斥,而多个写锁之间互斥,读锁和写锁之间也互斥。在没有写入操作时,读操作的多个Thread都可以获得读锁,而进行写操作,必须获得写锁,即多个Thread可以同时进行读操作,但是同一时刻,只能有一个线程进行写操作。

4.3 读读共享

主类:

public class ReadLock {
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void readSth(){
        try {
            lock.readLock().lock();
            System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
}

线程A:

public class A extends Thread {
    ReadLock readLock;

    public A(ReadLock readLock) {
        this.readLock = readLock;
    }

    @Override
    public void run() {
        readLock.readSth();
    }
}

线程B:

public class A extends Thread {
    ReadLock readLock;

    public A(ReadLock readLock) {
        this.readLock = readLock;
    }

    @Override
    public void run() {
        readLock.readSth();
    }
}

主类:


public class test2 {
    public static void main(String[] args) {
        ReadLock readLock = new ReadLock();
        A a = new A(readLock);
        B b = new B(readLock);
        a.start();
        b.start();
    }
}

结果:

Sat Aug 10 16:55:46 CST 2019进行读操作
Sat Aug 10 16:55:46 CST 2019进行读操作

说明:两个线程是同一时间进行操作的,所以操作读的多个线程可以同时拥有读锁。

4.4 写写互斥

测试类:

public class WriteLockTest {
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void readSth(){
        try {
            lock.writeLock().lock();
            System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}

结果:

Sat Aug 10 17:02:58 CST 2019进行读操作
Sat Aug 10 17:03:02 CST 2019进行读操作

说明:两个写操作是互斥进行的。

4.5 读写/写读互斥

测试类:

public class ReadWriteLockTest {
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void writeSth(){
        try {
            lock.writeLock().lock();
            System.out.println(new Date(System.currentTimeMillis()) + "进行写操作");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
    public void readSth(){
        try {
            lock.readLock().lock();
            System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
}

结果:

Sat Aug 10 17:07:08 CST 2019进行读操作
Sat Aug 10 17:07:12 CST 2019进行写操作

说明:

读写操作是互斥进行的。