Lock的使用

一、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进行写操作

说明:

读写操作是互斥进行的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值