Java 多线程学习笔记二十三(Lock锁)

内容来自B站【狂神说Java】多线程详解

Lock(锁)

  • 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步,同步锁使用Lock对象充当。
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
  • ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。
    使用到的地方如:CopyOnWriteArrayList类中final transient ReentrantLock lock = new ReentrantLock();
  • 使用lock.lock()加锁,lock.unlock()解锁,如果有异常,需要将lock.unlock()写在finally代码块中。

synchronized与Lock对比

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

代码演示

一、未使用Lock锁

package com.example.demo.thread.sync.lock;

public class TestLock {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        for (int i = 0; i < 20; i++) {
            new Thread(buyTicket, "用户" + i).start();
        }
    }

}

class BuyTicket implements Runnable {

    private int ticketNums = 10;

    @Override
    public void run() {
        try {
            this.buy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void buy() throws InterruptedException {
        // 模拟查询是否还有票
        Thread.sleep(1000);
        if (this.ticketNums > 0) {
            // 模拟买票耗时
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "买到了第 "+ this.ticketNums +" 张票,还剩 " + (--this.ticketNums) + "张票。");
        } else {
            System.out.println(Thread.currentThread().getName() + "没有买到票,已售罄。");
        }
    }

}

输出

用户0买到了第 3 张票,还剩 1张票。
用户7买到了第 0 张票,还剩 -1张票。
用户14买到了第 -7 张票,还剩 -8张票。
用户15买到了第 -6 张票,还剩 -7张票。
用户11买到了第 10 张票,还剩 5张票。
用户6买到了第 10 张票,还剩 8张票。
用户2买到了第 10 张票,还剩 9张票。
用户12买到了第 -3 张票,还剩 -5张票。
用户1买到了第 -3 张票,还剩 -4张票。
用户9买到了第 -5 张票,还剩 -6张票。
用户17买到了第 10 张票,还剩 3张票。
用户16买到了第 0 张票,还剩 -3张票。
用户4买到了第 10 张票,还剩 5张票。
用户18买到了第 10 张票,还剩 9张票。
用户13买到了第 10 张票,还剩 4张票。
用户19买到了第 10 张票,还剩 6张票。
用户8买到了第 1 张票,还剩 0张票。
用户10买到了第 3 张票,还剩 2张票。
用户5买到了第 0 张票,还剩 -2张票。
用户3买到了第 10 张票,还剩 7张票。

二、使用Lock锁

package com.example.demo.thread.sync.lock;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        for (int i = 0; i < 20; i++) {
            new Thread(buyTicket, "用户" + i).start();
        }
    }

}

class BuyTicket implements Runnable {

    private int ticketNums = 10;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            lock.lock();
            this.buy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void buy() throws InterruptedException {
        // 模拟查询是否还有票
        Thread.sleep(1000);
        if (this.ticketNums > 0) {
            // 模拟买票耗时
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "买到了第 "+ this.ticketNums +" 张票,还剩 " + (--this.ticketNums) + "张票。");
        } else {
            System.out.println(Thread.currentThread().getName() + "没有买到票,已售罄。");
        }
    }

}

输出

用户0买到了第 10 张票,还剩 9张票。
用户1买到了第 9 张票,还剩 8张票。
用户2买到了第 8 张票,还剩 7张票。
用户3买到了第 7 张票,还剩 6张票。
用户4买到了第 6 张票,还剩 5张票。
用户5买到了第 5 张票,还剩 4张票。
用户6买到了第 4 张票,还剩 3张票。
用户7买到了第 3 张票,还剩 2张票。
用户8买到了第 2 张票,还剩 1张票。
用户9买到了第 1 张票,还剩 0张票。
用户10没有买到票,已售罄。
用户11没有买到票,已售罄。
用户12没有买到票,已售罄。
用户13没有买到票,已售罄。
用户14没有买到票,已售罄。
用户15没有买到票,已售罄。
用户16没有买到票,已售罄。
用户17没有买到票,已售罄。
用户18没有买到票,已售罄。
用户19没有买到票,已售罄。
posted @ 2022-01-02 14:48  君子键  阅读(53)  评论(0)    收藏  举报