多线程之Lock锁

Lock锁

  1. 从JDK5.0开始,Java提供了更强大的线程同步机制,通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当。
  2. java.util.concurrent.locks.Lock 接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问。每次只能有一个线程对 Lock 对象加锁,线程开始访问共享资源前都应先获得Lock对象。
  3. ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显式加锁,释放锁。
  4. ReentrantLock 称为可重入锁

下面通过代码来演示一下

package org.javaboy.juc;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: bai
 * @DateTime: 2020/6/7 21:02
 */
public class TestLock implements Runnable {

    int ticketNums = 10;
    private final ReentrantLock lock = new ReentrantLock(); // 定义lock锁

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();// 显式加锁
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(100); // 模拟延时操作
                        System.out.println(Thread.currentThread().getName() + "::" + ticketNums--);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            } finally {
                lock.unlock();  // 显示解锁
            }

        }
    }

    public static void main(String[] args) {
        TestLock testLock = new TestLock();

        new Thread(testLock, "小明").start();
        new Thread(testLock, "小李").start();
        new Thread(testLock, "黄牛党").start();
    }
}

代码解释:首先我们模拟多人抢票的场景,首先规定票数只有10张,然后我们通过票数判断是否已经被购完。在没有加入 ReentrantLock 之前,我们模拟的这个抢票场景会出现数据不一致问题,然后我们通过定义 ReentrantLock 锁,通过 lock.lock() 方法来显式加锁,通过 lock.unlock() 方法来显式解锁。这样就可以保证我们的数据一致性问题。
注意:一般我们显示加锁和解锁建议写在try/catch块中,这样就算发生了异常,finall 块依旧能够执行,我们的锁也可以被释放。这样就保证了程序的最起码的健壮性。

synchronized 与 Lock 的对比

  • Lock 是显式锁(手动开启和关闭),synchronized 是隐式锁,出了作用域自动释放。
  • Lock 只有代码块锁,synchronized 有代码块锁和方法锁。
  • 使用 Lock 锁,JVM 将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的字类)
  • 优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了响应资源)> 同步方法(在方法体之外)

你可能感兴趣的:(Java)