zoukankan      html  css  js  c++  java
  • 显示锁Lock

    Lock简单介绍

    public class LockDemo {
    
        // 声明一个lock锁
        private Lock lock = new ReentrantLock();
    
        private int count;
    
        // 用lock来加锁
        public void increament01() {
            // 加锁
            lock.lock();
            try {
                count++;
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
    
        // syn关键字加锁
        public synchronized void increament02() {
            count++;
        }
    
    }
    

    释放锁的代码应当放在finally关键字中,以保证代码出现异常后,锁能够及时的释放掉。

    syn和lock的比较

    1. syn称之为内置锁,因为它是一个关键字,在jdk中没有源码,经过多年优化,性能很优秀,并且代码较为简洁,但较lock有局限性。
    2. lock称之为显示锁,在获取锁的过程中可以被中断,并且还可以超时获取锁,和尝试加锁,syn则没有这三个特点。
    3. syn和lock都是可重入锁。
    4. ReentrantLock和syn关键字都是排他锁,即在同一个时刻只允许一个线程访问。

    总结:如果没有第二条中的业务场景的时候,应该用syn关键字。

    公平锁和非公平锁

    如果在时间上,先对锁进行获取的请求,一定先被满足,这个锁就是公平的,如果不满足,这个锁就不非公平的,一般来说,非公平锁效率相对较高。

        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    源码看出,lock默认是非公平的锁。

    Lock和Condition

    举例说明:

    下面是一个快递类:一个lock下面可以有多个condition,因为需要监控两个条件的变化,所以定义了两个condition来锁这两个变量。lock锁不会自动释放,需要手动释放。使线程进入等待状态的方法不是object的wait方法,而是condition接口中的await方法,唤醒线程的方法也是condition接口中的signal和signalAll,这点很重要。

    public class Express {
        // 声明锁
        private Lock lock = new ReentrantLock();
        // 公里变化锁
        private Condition kmCondition = lock.newCondition();
        // 地点变化锁
        private Condition siteCondition = lock.newCondition();
        // 始发地
        private final static String CITY = "ShangHai";
        // 里程变化
        private int km;
        // 地点变化
        private String site;
    
        Express() {
    
        }
    
        Express(int km, String site) {
            this.km = km;
            this.site = site;
        }
    
        // 里程数变化,会唤起线程
        public void changeKm() {
            lock.lock();
            try {
                this.km = 101;
                // 唤醒
                kmCondition.signal();
            } finally {
                lock.unlock();
            }
        }
    
        // 地点变化会唤起线程
        public void changeSite() {
            lock.lock();
            try {
                this.site = "BeiJing";
                // 唤醒
                siteCondition.signal();
            } finally {
                lock.unlock();
            }
        }
    
        // 用来监听里程数的变化
        public void waitKm() {
            lock.lock();
            try {
                while (this.km <= 100) {
                    System.out.println(Thread.currentThread().getId() + "监控【公里数】变化的线程即将进入等待状态。。。");
                    kmCondition.await();
                    System.out.println(Thread.currentThread().getId() + "-号监听===里程变化===的线程被唤醒了。。。");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getId() + "释放了锁。");
            }
            System.out.println(Thread.currentThread().getId() + "-号监听===里程变化===的线程去做相应的事了");
        }
    
        // 用来监听地点的变化
        public void waitSite() {
            lock.lock();
            try {
                while (CITY.equals(this.site)) {
                    System.out.println(Thread.currentThread().getId() + "监控【地点】变化的线程即将进入等待状态。。。");
                    siteCondition.await();
                    System.out.println(Thread.currentThread().getId() + "-号监听===地点变化===的线程被唤醒了。。。");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getId() + "释放了锁。");
            }
            System.out.println(Thread.currentThread().getId() + "-号监听===地点变化===的线程去做相应的事了");
        }
    
    }
    

    main方法测试类:

    分别启动三个监控地点变化的线程和三个监控公里数变化的线程,并使它们都进入等待状态,然后改变地点变化。调用await方法后,每个被阻塞的线程都会加到队列末尾排队。

    public class Test {
        // 初始化快递
        private static Express express = new Express(0, "ShangHai");
        // 用来监听里程数变化的线程
        static class CheckKm implements Runnable {
            public void run() {
                express.waitKm();
            }
        }
    
        // 用来监听地点变化的线程
        static class CheckSite implements Runnable {
            public void run() {
                express.waitSite();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            // 启动三个线程去监听里程数的变化
            for (int i = 0; i <= 2; i++) {
                new Thread(new CheckKm()).start();
            }
    
            // 启动三个线程去监听地点的变化
            for (int i = 0; i <= 2; i++) {
                new Thread(new CheckSite()).start();
            }
    
            // 主线程睡眠一秒,异常信息抛出去
            Thread.sleep(1000);
    
            // 让快递的地点发生变化
            express.changeSite();
        }
    }
    
    1. 首先使用的是用signal来唤醒线程:唤醒在此Lock对象上等待的单个线程。
    11监控【公里数】变化的线程即将进入等待状态。。。
    13监控【公里数】变化的线程即将进入等待状态。。。
    14监控【地点】变化的线程即将进入等待状态。。。
    15监控【地点】变化的线程即将进入等待状态。。。
    12监控【公里数】变化的线程即将进入等待状态。。。
    16监控【地点】变化的线程即将进入等待状态。。。
    14-号监听===地点变化===的线程被唤醒了。。。
    14释放了锁。
    14-号监听===地点变化===的线程去做相应的事了
    

    signal是唤醒当前阻塞队列中的第一个,而不是随机唤醒。

    1. 使用signalAll来唤醒线程:唤醒在此Lock对象上等待的所有线程。
    11监控【公里数】变化的线程即将进入等待状态。。。
    15监控【地点】变化的线程即将进入等待状态。。。
    13监控【公里数】变化的线程即将进入等待状态。。。
    12监控【公里数】变化的线程即将进入等待状态。。。
    14监控【地点】变化的线程即将进入等待状态。。。
    16监控【地点】变化的线程即将进入等待状态。。。
    15-号监听===地点变化===的线程被唤醒了。。。
    15释放了锁。
    15-号监听===地点变化===的线程去做相应的事了
    14-号监听===地点变化===的线程被唤醒了。。。
    14释放了锁。
    14-号监听===地点变化===的线程去做相应的事了
    16-号监听===地点变化===的线程被唤醒了。。。
    16释放了锁。
    16-号监听===地点变化===的线程去做相应的事了
    

    signalAll唤醒所有当前队列中的等待线程。

  • 相关阅读:
    【Luogu1501】Tree(Link-Cut Tree)
    【BZOJ3530】数数(AC自动机,动态规划)
    【BZOJ1212】L语言(AC自动机)
    【BZOJ2037】Sue的小球(动态规划)
    【BZOJ1899】午餐(动态规划)
    【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)
    【BZOJ1040】骑士(动态规划)
    【BZOJ1969】航线规划(Link-Cut Tree)
    【BZOJ4653】【NOI2016】区间(线段树)
    我也不知道什么是"莫比乌斯反演"和"杜教筛"
  • 原文地址:https://www.cnblogs.com/zhangjianbing/p/13574812.html
Copyright © 2011-2022 走看看