【Java】JUC并发编程-Lock锁

一、概述

Lock锁是一个类似于Synchronized的线程同步机制,但是Lock比Synchronized更加灵活。Lock是个接口,有两个实现类:ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)

二、Lock和Synchronized的区别

  1. lock是一个接口,synchronized是Java中的关键字
  2. synchronized不需要用户去手动释放锁,发生异常或者线程结束时自动释放锁;lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象
  3. lock可以让等待锁的线程响应中断,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
    在这里插入图片描述

三、Lock锁的API

在这里插入图片描述

四、ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)

1、ReentrantLock(重入锁)

重入锁也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。

public class ReentrantDemo implements Runnable {
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        set();
    }
    public void set() {
    
    
        try {
    
    
            lock.lock();
            System.out.println("set 方法");
            get();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();// 必须在finally中释放
        }
    }

    public void get() {
    
    

        try {
    
    
            lock.lock();
            System.out.println("get 方法");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public static void main(String[] args) {
    
    
        ReentrantDemo reentrantDemo = new ReentrantDemo();
        new Thread(reentrantDemo).start();
    }

测试结果:同一个线程,首先在set方法中获取锁,然后调用get方法,get方法中重复获取同一个锁。两个方法都执行成功
在这里插入图片描述

2、ReentrantReadWriteLock(读写锁)

读写锁,可以分别获取读锁或写锁。也就是说将数据的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。读锁使用共享模式;写锁使用独占模式;读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

  • writeLock():获取写锁。
  • readLock():获取读锁。

执行三个线程进行读写操作,并设置一个屏障,线程依次准备就绪后未获取锁之前都在等待,当第三个线程执行
cyclicBarrier.await()后屏障解除,三个线程同时执行。

public class WriteAndReadLockTest {
    
    
    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10,
            60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    private static int i = 100;
    public static void main(String[] args) {
    
    
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            write(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.shutdown();
    }

    private static void read(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.readLock().lock();
        try {
    
    
            System.out.println("读线程 "+ thread.getName() + " 开始执行, i=" + i);
            Thread.sleep(1000);
            System.out.println(thread.getName() +" is over!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            reentrantReadWriteLock.readLock().unlock();

        }
    }
    private static void write(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.writeLock().lock();
        try {
    
    
            i++;
            System.out.println("写线程 "+ thread.getName() + " is doing, i=" + i);
            System.out.println(thread.getName() +" is over!");
        } finally {
    
    
            reentrantReadWriteLock.writeLock().unlock();
        }
    }
}

执行结果:线程3先获取到了读锁,因为读锁时是可以共享的,所以线程1也可以获取到读锁,线程1、线程3读操作完成后,将读锁释放后,线程2才能获取到写锁并开始执行写操作
在这里插入图片描述

五、Lock锁的API代码实现

1、lock()、unLock()

public class LockAndUnlock  implements Runnable{
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        //上锁
        lock.lock();
        try {
    
    
            System.out.println("ThreadA获取锁");
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock(); // 解锁
            System.out.println("ThreadA释放锁");
        }
    }

    public static void main(String[] args) {
    
    
        LockAndUnlock lockAndUnlock = new LockAndUnlock();
        Thread threadA = new Thread(lockAndUnlock,"threadA");
        threadA.start();
    }
}

执行结果:
在这里插入图片描述

2、lockInterruptibly()

实例:当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

public class LockInterruptiblyDemo {
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadA获得锁 ");
                    // 模拟线程A持有锁的一段时间
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadA在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadB获得锁");
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadB在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();

        // 在线程B等待锁的过程中,中断线程B
        threadB.interrupt();
    }
}

执行结果:
在这里插入图片描述

3、tryLock()

实例:当两个线程同时通过lock.tryLock()想获取某个锁时,假若此时线程A获取到了锁,而线程B不会等待,直接放弃获取锁

public class TryLock{
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadA获得了锁");
                        // 模拟线程A持有锁的一段时间
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadA获取锁失败");
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadB获得了锁");
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadB获取锁失败");
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();
    }
}

执行结果:
在这里插入图片描述

4、tryLock(long time, TimeUnit unit)

如果锁定可用,则此方法立即返回值true。
如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
● 当前线程获取锁。
● 其他一些线程中断当前线程。
● 等待时间过去了,返回false

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
                Thread.sleep(1000 * 4);
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (Thread.holdsLock(lock)) {
    
    
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();
    }
}

执行结果:
在这里插入图片描述

如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态,当前线程是可以被中断的。

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁");
                for (int i = 0; i <= 500000000; i++) {
    
    
                    if (i == 500000000) {
    
    
                        System.out.println("运算结束");
                    }
                }
                lock.unlock();
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            System.out.println(Thread.currentThread().getName() +"中断");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();


        try {
    
    
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadA.interrupt();
        threadB.interrupt();
    }
}

执行结果:threadA获得锁,其他线程休眠,然后被中断
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45490023/article/details/132076149