《java多线程编程核心技术》 第4章 Lock的使用

知识点:

  1. ReentrantLock类的使用
  2. ReentrantReadWriteLock类的使用

4.1 使用ReentrantLock类

ReentrantLock不仅能够实现和synchronized同样的效果,还具有嗅探锁定,多路分支通知等等。

package demo14;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod(){
        lock.lock();
        try {
            for (int i = 0; i <5 ; i++) {
                System.out.println("线程名称"+Thread.currentThread().getName()+"   "+(i+1));
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo14;

public class MyThread extends Thread {

    private MyService myService;

    public MyThread(MyService myService){
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}

package demo14;

public class Run {

    public static void main(String[] args) {

        MyService myService = new MyService();
        MyThread myThread1 = new MyThread(myService);
        MyThread myThread2 = new MyThread(myService);
        MyThread myThread3 = new MyThread(myService);
        MyThread myThread4 = new MyThread(myService);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
    }

}

在这里插入图片描述

4.1.3 使用Condition实现等待通知

package demo15;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();
    private Condition condition =lock.newCondition();

    public void await(){
        lock.lock();
        try {
            System.out.println("await时间 "+System.currentTimeMillis());
            //等待
            condition.await();
            System.out.println("await下面 ");

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signal(){
        lock.lock();
        try {
            System.out.println("signal时间 "+System.currentTimeMillis());
            //通知
            condition.signal();
            System.out.println("signal下面 ");

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}
package demo15;


public class MyThread extends Thread {

    private MyService myService;

    public MyThread(MyService myService){
        this.myService = myService;
    }

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

package demo15;

public class Run {
    public static void main(String[] args) {

        try {
            MyService myService = new MyService();
            MyThread myThread1 = new MyThread(myService);
            myThread1.start();
            Thread.sleep(5000);

            myService.signal();
        }catch (Exception ex){
            ex.printStackTrace();
        }

    }

}

在这里插入图片描述

4.1.4 使用多个Condition实现通知部分线程

package demo16;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();

    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();

    public void awaitA(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" awaitA =========== 进入lock锁");
            conditionA.await();
            System.out.println(Thread.currentThread().getName()+" awaitA =========== 等待结束。。。");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" awaitB =========== 进入lock锁");
            conditionB.await();
            System.out.println(Thread.currentThread().getName()+" awaitB =========== 等待结束。。。");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalAll_B(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" signalAll =========== 进入lock锁");
            conditionB.signal();
            conditionA.signal();
            System.out.println(Thread.currentThread().getName()+" signalAll =========== 通知完毕。。。");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo16;

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA (MyService myService){

        this.myService = myService;
    }

    @Override
    public void run() {

        myService.awaitA();
    }
}

package demo16;

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){

        this.myService = myService;
    }

    @Override
    public void run() {

        myService.awaitB();
    }
}

package demo16;

public class Run {

    public static void main(String[] args) {

        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(myService);
        threadB.setName("B");
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myService.signalAll_B();
    }
}

在这里插入图片描述
这样便实现了唤醒部分指定的线程。

4.1.5 公平锁和非公平锁

公平锁:
表示线程获取锁的顺序是按照线程加锁的顺序来分配的,也就是先来先得到锁。
非公平锁:
就是一种获取锁的抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,很有可能造成一些线程一直拿不到锁。
设置方式:
采用如下构造方法创建互斥锁,true表示公平锁,false表示非公平锁,默认是false。
在这里插入图片描述
案例如下:

package demo17;

import java.util.concurrent.locks.ReentrantLock;

public class Service  {

    private ReentrantLock lock;

    public Service(boolean isFair){

        //isFair 为true则是公平锁 为false则是非公平 默认是false
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){

        lock.lock();
        try {
            System.out.println("线程"+Thread.currentThread().getName()+" 获得锁");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo17;

public class RunFair {

    public static void main(String[] args) {

        Service service = new Service(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"运行了");
                service.serviceMethod();
            }
        };

        Thread[] threads = new Thread[100];
        for (int i = 0; i <100 ; i++) {
            threads[i] = new Thread(runnable);
        }
        for (int i = 0; i <100 ; i++) {
            threads[i].start();
        }
    }
}

4.1.6 几个方法的简单说明:

getHoldCount() 返回当前线程调用lock()的次数。
getQueueLength() 返回等待当前锁的线程。
getWaitQueueLength(Condition condition) 返回执行当前condition的await()的线程数。
hasQueuedThread(Thread thread) 查询指定的线程是否正在等待获取此锁。
hasQueuedThreads() 查询是否有线程正在等待获取此锁。
hasWaiters(Condition condition) 查询是否有线程正在等待与此锁有关的condition条件。
isFair() 判断是否公平锁。
isHeldByCurrentThread() 查询当前线程是否保持此锁。
isLocked() 查询此锁是否由任意线程保持着。
lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。
tryLock() 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
tryLock(long timeout, TimeUnit unit) 如果锁在给定的时间内没有被另一个线程保持,且当前线程尚未被中断,则获取该锁定。

4.1.7 使用Condition实现顺序执行。

使用Condition对象可以对线程执行的业务进行排序。

package demo18;

public class F {

    volatile public static int count = 1;
}

package demo18;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Run {

    volatile private static int count =1;

    private  static ReentrantLock lock = new ReentrantLock();
    final private  static Condition conditionA = lock.newCondition();
    final private  static Condition conditionB = lock.newCondition();
    final private  static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {

        Thread threadA = new Thread(() -> {
            lock.lock();
            try {
                while (count!=1){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadA"+ (i+1));
                }
                count = 2;
                conditionB.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }
        });

        Thread threadB = new Thread(() -> {
            lock.lock();
            try {
                while (count!=2){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadB"+ (i+1));
                }
                count = 3;
                conditionC.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }

        });

        Thread threadC = new Thread(() -> {
            lock.lock();
            try {
                while (count!=3){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadC"+ (i+1));
                }
                count = 1;
                conditionA.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }

        });
        Thread[] aArray = new Thread[5];
        Thread[] bArray = new Thread[5];
        Thread[] cArray = new Thread[5];
        for (int i = 0; i <5 ; i++) {

            aArray[i] = new Thread(threadA);
            bArray[i] = new Thread(threadB);
            cArray[i] = new Thread(threadC);
            aArray[i].start();
            bArray[i].start();
            cArray[i].start();
        }
    }

}

在这里插入图片描述

4.2 使用ReentrantReadWriteLock类

互斥锁ReentrantLock具有完全互斥排他的效果,也就是说,同一时间只有一个线程来访问lock的代码,尽管保证了实例变量的线程安全性,但是效率低下。
读写互斥锁ReentrantReadWriteLock,有两个锁,读锁和写锁。读操作的锁称为共享锁,写操作的锁称为排他锁。也就是说在读的时候线程之间是不互斥的,但是只要有写参与的操作均互斥。

4.2.1 读读共享

package demo19;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){

        lock.readLock().lock();
        try {
            System.out.println("获取读锁"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
}

package demo19;

public class ThreadA  extends Thread{

    Service service;
    public ThreadA (Service service){

        this.service = service;
    }
    @Override
    public void run() {

        service.read();
    }
}

package demo19;

public class ThreadB extends Thread{

    Service service;
    public ThreadB(Service service){

        this.service = service;
    }
    @Override
    public void run() {

        service.read();
    }
}

package demo19;

public class Run {

    public static void main(String[] args) {

        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述
上图表示两个线程同时进入锁的代码。

扫描二维码关注公众号,回复: 8645077 查看本文章

4.2.2 写写互斥锁

package demo20;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void write(){

        lock.writeLock().lock();
        try {
            System.out.println("获取写锁"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}

在这里插入图片描述
使用写锁代码lock.writeLock()的效果就是同一时间只允许一个线程执行lock()的代码。

4.2.2 读写互斥锁

package demo21;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){

        lock.readLock().lock();
        try {
            System.out.println("获取读锁"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }

    public void write(){

        lock.writeLock().lock();
        try {
            System.out.println("获取写锁"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}

然后两个线程分别调用。
在这里插入图片描述

总结:
在这次的学习中完全可以用Lock对象将关键字synchronized替换掉, 它具有很多synchronized没有的功能。

发布了157 篇原创文章 · 获赞 75 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/wu2374633583/article/details/103927061