Java中的Lock接口
Synchronized & Lock
- synchronized 是Java语言中的关键字,由monitorenter,monitorexit两个指令实现。JVM会将monitorenter指定插在同步代码块开始的地方,将monitorexit指定插在同步代码快结束和出现异常的地方。 
- Lock是JUC包下的组件, 是基于AQS(队列同步器)实现的。 
- synchronized功能与ReentrantLock类相对应, 都是可重入的锁。 
- Lock与synchronized关键字相比,实现了公平锁和非公平锁,synchronized关键字是非公平的。同时Lock接口提供了在获取锁被阻塞时可响应中断以及超时获取锁的API。 
- Lock接口可以绑定多个条件,即绑定多个 Condition 对象,这样唤醒时可以唤醒指定条件上的线程。如生产者消费者例子,使用 synchronized 关键字时,当生产者生产消息时,需要唤醒消费者线程,我们只能调用notifyAll 方法,这样不仅会唤醒消费者端的线程而且还会唤醒生产者自己这一端的线程。 
**注: **
- 公平锁是指多个线程等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,而非公平锁不保证这一点。在锁被释放时,任何一个等待锁的线程都有机会获取锁。
- ReentrantLock实现的非公平锁只能保证阻塞队列里最早等待的线程与新来的线程竞争抢锁, 对于阻塞队列其它的线程依然需要等待,因为队列是先进先出的,只有该线程获取锁然后释放锁后后续节点才有资格竞争锁。
Lock 接口的 API
public interface Lock {
    // 获取锁直到获取锁成功后返回,否则被阻塞,即使被中断也不会返回
    void lock();
    // 获取锁并且响应中断,注意如果t线程因为竞争锁失败而被阻塞,另外一个线程中断了t线程
    // 那么t线程会被唤醒,并且抛出中断异常,清除中断状态, 阻塞线程的方法是调用LockSupport.park()
    // 当调用了LockSupport.unpark或者中断了线程,线程会从park方法中唤醒。
    void lockInterruptibly() throws InterruptedException;
    // 尝试获取锁,不管成功与否都返回
    boolean tryLock();
    // 超时获取锁,此方法返回的情况如下:
    // 1. 在给定时间内竞争到锁, 返回true
    // 2. 超时时间过了, 返回false
    // 3. 线程被中断,抛出中断异常,清除中断状态
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 释放锁,如果当前线程没有获取到锁,调用此方法将会抛出IllegalMonitorStateException
    void unlock();
    // 创建Condition对象,提供了类似synchronized 锁对象的wait, notify, notifyAll方法
    Condition newCondition();
}
public interface Condition {
    // 使获取锁的线程阻塞,并且释放锁,直到另一个线程中断了此线程或者调用了signal,signalAll方法
    // 没有获取锁的线程调用此方法会抛出IllegalMonitorStateException
    void await() throws InterruptedException;
    // 使获取的线程阻塞,并且释放锁,但是不响应中断请求,只有调用了signal,signalAll才会被唤醒
    // 其实是线程中断后醒过来,再次被阻塞-LockSupport.park()
    void awaitUninterruptibly();
    // 使获取的线程阻塞,并且释放锁, 被唤醒的原因如下:
    // 1. 超时时间已过  2. 线程被中断   3. 有线程调用了signal,signalAll方法
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    // 唤醒最开始等待在条件队列上的线程
    void signal();
    // 唤醒所有等待在条件队列上的线程
    void signalAll();
}
注: 所有从await中醒过来的线程只有重新获取到锁才能往下执行,否则依然会在同步队列中等待获取锁
Lock的使用
下面将使用ReentrantLock实现一个简单的阻塞队列。
public class BlockQueue {
    private Lock lock = new ReentrantLock();
    private Condition full = lock.newCondition();
    private Condition empty = lock.newCondition();
    private Queue<String> queue;
    private int capacity;
    public BlockQueue(int capacity) {
        queue = new ArrayDeque<>(capacity);
        this.capacity = capacity;
    }
    public void put(String element) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                try {
                    full.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue.add(element);
            empty.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public String take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    empty.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            String element = queue.remove();
            full.signalAll();
            return element;
        } finally {
            lock.unlock();
        }
    }
}
测试Lock接口的方法
public class LockTest {
    private final Object lock = new Object();
    /**
     * 测试synchronized关键字
     * synchronized: 当线程在获取锁时, 竞争失败的线程会处于阻塞, 并且不响应中断直至获取到锁
     *
     * 程序结果:
     * --------------主线程准备释放锁---------------------
     * 是否被中断: true
     * block-thread获取锁成功
     */
    @Test
    public void testSynchronized() {
        // 当前线程持有锁
        synchronized (lock) {
            // 开启另外一个线程尝试获取锁
            Thread t = new Thread(()->{
                //阻塞
                synchronized (lock) {
                    System.out.println("是否被中断: " + Thread.currentThread()
                                       .isInterrupted());
                    System.out.println(Thread.currentThread().getName() + "获取锁成功");
                }
            }, "block-thread");
            t.start();
            //休眠一秒, 让开启的线程充分运行, 接着进行中断
            sleep(1);
            t.interrupt();
            //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
            sleep(10);
            System.out.println("--------------主线程准备释放锁---------------------");
        }
    }
    /**
     * 测试lock.lock()同synchronized关键字
     *
     * 程序结果:
     * --------------主线程准备释放锁---------------------
     * 是否被中断: true
     * block-thread获取锁成功
     */
    @Test
    public void testLock() {
        //当前线程持有锁
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            // 开启另外一个线程尝试获取锁
            Thread t = new Thread(()->{
                //阻塞
                lock.lock();
                try {
                    System.out.println("是否被中断: " + Thread.currentThread()
                                       .isInterrupted());
                    System.out.println(Thread.currentThread().getName() + "获取锁成功");
                } finally {
                    lock.unlock();
                }
            }, "block-thread");
            t.start();
            //休眠一秒, 让开启的线程充分运行, 接着进行中断
            sleep(1);
            t.interrupt();
            //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
            sleep(10);
            System.out.println("--------------主线程准备释放锁---------------------");
        } finally {
            lock.unlock();
        }
    }
    /**
     * 测试lock.lockInterruptibly(): 抛出中断异常
     * 当线程被中断后,线程会从LockSupport.park()中醒过来,然后会检查自己是否被中断,如果被中断过
     * 则抛出中断异常,清除中断状态。
     *
     * 程序结果: block-thread is interrupted! exit
     */
    @Test
    public void testlockInterruptibly() {
        // 当前线程持有锁
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            // 开启另外一个线程尝试获取锁
            Thread t = new Thread(()->{
                //阻塞
                try {
                    lock.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获取锁成功");
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() +
                                       "is interrupted! exit");
                } finally {
                    if(lock.isHeldByCurrentThread())
                        lock.unlock();
                }
            }, "block-thread");
            t.start();
            //休眠一秒, 让开启的线程充分运行, 接着进行中断
            sleep(1);
            t.interrupt();
            //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会相应中断
            sleep(10);
        } finally {
            lock.unlock();
        }
    }
    private void sleep(int second) {
        try {
            TimeUnit.SECONDS.sleep(second);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Java中的Lock接口的更多相关文章
- Java中的Lock与synchronized
		并发编程学习笔记之Lock与synchronized 一.什么是可重入锁 Lcok在Java中是一个接口,一般在面试问题中问到的可能是ReentrantLock与synchronized的区别.Ree ... 
- Java 中的集合接口——List、Set、Map
		Java 中的集合接口——List.Set.Map 什么叫集合:集合就是Java API所提供的一系列类的实例,可以用于动态存放多个对象.这跟我们学过的数组差不多,那为什么我们还要学集合,我们看看数组 ... 
- 转:二十一、详细解析Java中抽象类和接口的区别
		转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ... 
- 关于JAVA中抽象类和接口的区别辨析
		今天主要整理一下新学习的有关于Java中抽象类和接口的相关知识和个人理解. 1 抽象类 用来描述事物的一般状态和行为,然后在其子类中去实现这些状态和行为.也就是说,抽象类中的方法,需要在子类中进行重写 ... 
- java中的标记接口(标签接口)
		Java中的标记接口(Marker Interface),又称标签接口(Tag Interface),具体是不包含任何方法的接口.在Java中很容易找到标记接口的例子,比如JDK中的Serialzab ... 
- Java中的Serializable接口和transient关键字
		Java中的Serializable接口和transient关键字 Table of Contents 1. 向memcached中放数据时遇到NotSerializableException异常 2 ... 
- 一文带你看懂Java中的Lock锁底层AQS到底是如何实现的
		前言 相信大家对Java中的Lock锁应该不会陌生,比如ReentrantLock,锁主要是用来解决解决多线程运行访问共享资源时的线程安全问题.那你是不是很好奇,这些Lock锁api是如何实现的呢?本 ... 
- 用好JAVA中的函数式接口,轻松从通用代码框架中剥离掉业务定制逻辑
		大家好,又见面了. 今天我们一起聊一聊JAVA中的函数式接口.那我们首先要知道啥是函数式接口.它和JAVA中普通的接口有啥区别?其实函数式接口也是一个Interface类,是一种比较特殊的接口类,这个 ... 
- Java Concurrency API 中的 Lock 接口(Lock interface)  是什么?对比同步它有什么优势?
		Lock 接口比同步方法和同步块提供了更具扩展性的锁操作. 他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的 条件对象. 它的优势有: 可以使锁更公平 可以使线程在等待锁的时候响 ... 
随机推荐
- CentOS内网机器利用iptables共享公网IP上网
			公司有个业务是2B的以及日活不大,所以两台服务器搞定,一个6M EIP.两台机器都是CentOS7系统EIP为 xxx.xxx.xxx.xxx绑在 内网ip为 172.18.30.175的服务器上,内 ... 
- Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。
			借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ... 
- java 自动补全
			int youNumber = 1; // 0 代表前面补充0 // 4 代表长度为4 // d 代表参数为正数型 String str = String.format("%04d" ... 
- Spring再接触 注入类型
			共有三种注入类型 一种是set注入 一种是构造注入 一种是接口注入 最常用的还是set 现在看一下construct 构造注入 在userservice中加入 package com.bjsxt.se ... 
- # 20175213 2018-2019-2 《Java程序设计》第2周学习总结
			## 教材学习内容总结 在第二周的学习过程中,我学习了第二章和第三章的内容.第二章中,我学习了基本数据类型和类型转换运算以及与C语言有着相同和不同的数组.标识符由字母,下划线,美元符号和数字组成,并且 ... 
- matplotlib 坑
			1 archlinux里安装好matplotlib之后一定要安装python-cario pacman -S python-cairo 
- oracle数据库分组之后取最大或者最小值
			原始数据 --使用子查询 SELECT * FROM student s WHERE s.birthday IN (SELECT MIN(s2.birthday) FROM student s2 GR ... 
- Python设计模式 - 基础 - 类/接口之间的六种关系
			在程序中需要把世间万物抽象成相应的类,现实世界中物与物之间的关系和程序中类与类之间的关系相对应,因为世间万物是普遍联系的,所以程序中类与类之间也不是孤立的.在系统分析和框架设计中,根据面向对象机制的三 ... 
- KALI安装与环境配置
			2018-2019 201899224<网络攻防实践>第二周作业 虚拟化网络攻防实验环境包括以下部分: 靶机:包含系统和应用程序安全漏洞,并作为攻击目标的主机.(Windows XP和Li ... 
- 测验2: Python基础语法(上) (第4周)
			快乐的数字 描述 编写一个算法来确定一个数字是否“快乐”. 快乐的数字按照如下方式确定:从一个正整数开始,用其每位数的平方之和取代该数,并重复这个过程,直到最后数字要么收敛等于1且一直等于1,要么将无 ... 
