Lock与synchronized

  Lock和synchronized在功能上是一样的。不过Lock提供了一些其他功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁

从性能上Lock的实现类ReentrantLock在JDK5.0之前要好于synchronized,在JDK6.0之后,synchronized做了优化,所以两者的性能相差无几了。

那在使用上应该选择哪个呢?在《Java并发编程实战》中有一句话:"仅当内置锁不能满足需求时,才可以考虑使用ReentrantLock"。在一些内置锁无法

满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可

中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized。

  在JDK5.0中,内置锁与ReentrantLock相比还有另外一个优点:在线程转储中能给出在哪些调用帧中获得了哪些锁,并能够检测出发生死锁的线程。JVM

并不知道哪些线程持有ReentrantLock,因此在调试使用ReentrantLock的线程问题时,将起不到帮助作用。JDK6.0解决了这个问题,它提供了一个管理和调试

接口,锁可以通过该接口进行注册,从而与ReentrantLock相关的加锁信息就能出现在线程转储中,并通过其他的管理接口和调试接口来访问。

  在内置锁中,死锁是一个很严重的问题,恢复程序唯一的方法是重新启动程序,而防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。

ReentrantLock有可定时与可轮询的功能,这就从另一个方面避免了死锁的发生(注意,使用ReentrantLock一定要在finally中释放锁)。

Lock的实现类ReentrantLock(重入锁)

  重入锁ReentrantLock,顾名思义,就是支持重进入的锁(synchronized隐式的支持重进入),它表示该锁能够支持一个线程对资源的重复加锁。除此之外,

该锁还支持获取锁时的公平和非公平性选择。

  1、ReentrantLock的基本使用。

package org.burning.sport.javase.thread.reentrantlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockTest implements Runnable{
private static Lock lock = new ReentrantLock();
private int i; @Override
public void run() {
while(true) {
increment();
}
} public void increment() {
try {
lock.lock();
i++;
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
//一定要在finally这里解锁,否则就是定时炸弹
lock.unlock();
}
} public static void main(String[] args) {
ReentrantLockTest test = new ReentrantLockTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
Thread t3 = new Thread(test); t1.setName("线程一:");
t2.setName("线程二:");
t3.setName("线程三:"); t1.start();
t2.start();
t3.start();
}
}

  2、锁的公平性

    如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。ReentrantLock提供了一个构造函数,能够控制锁是否是公平的。

  public ReentrantLock(boolean fair); 默认是非公平的。公平锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换影响性能,非公平的锁则会造成线程的 “饥饿”。

  3、Lock的中断响应

    线程的中断 中可以看到,Lock是可以响应线程触发的中断的。只要在获取锁的时候用 lock.lockInterruptibly();就可以了。

  4、轮询锁与定时锁

    lock.tryLock();  和 lock.tryLock(5, TimeUtil.SECONDS);  在内置锁中,死锁是一个很严重的问题,恢复程序唯一的方法是重新启动程序,而防止死锁的唯一方法就是在

  构造程序时避免出现不一致的锁顺序。ReentrantLock有可定时与可轮询的功能,这就从另一个方面避免了死锁的发生(注意,使用ReentrantLock一定要在finally中释放锁)。 

public class TryLockTest implements Runnable {
private Lock lock = new ReentrantLock(); @Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
lock.lock();
//do something .....
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
} }
}

  5、ReentrantLock的好搭档Condition条件

    Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的new Condition()方法)

  创建出来的,换句话说,Condition是依赖Lock对象的。

  示例代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ConditionUseCase {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition(); public void conditionWait() throws InterruptedException {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
} public void conditionSignal() throws InterruptedException {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
}

  Condition的(部分)方法及描述:

  void await() throws InterruptedException

  当前线程进入等待状态直到被通知(signal)或中断,当前线程将进入运行状态且从await()方法返回的情况,包括:

  其他线程调用该Condition的signal()或signalAll()方法,而当前线程被选中唤醒

     □ 其他线程(调用interrupt()方法)中断当前线程;

  □ 如果当前等待线程从await()方法返回,那么表明该线程已经获取了Condition对象所对应的锁;

void awaitUninterruptibly()

  当前线程进入等待状态直到被通知,从方法名称上可以看出该方法对中断不敏感

  long awaitNanos(long nanosTimeout) throws InterruptedException

  当前线程进入等待状态直到被通知、中断或者超时。返回值表示剩余的时间,如果在nanosTimeout纳秒

    之前被唤醒,那么返回值就是(nanosTimeout - 实际耗时) ,如果返回值是0或者负数,那么可以认定已经超时了

  boolean awaitUntil(Date deadline) throws InterruptedException

  当前线程进入等待状态直到被通知、中断或者到某个时间。如果没有到指定时间就被通知,方法返回true,否则,

  表示到了指定时间,方法返回false

   void signal()  唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁

  void signalAll()  唤醒所有等待在Condition上的线程,能够从等待方法返回的线程必须获得与Condition相关联的锁。

  示例代码:

  https://gitee.com/play-happy/base-project/blob/developer/src/main/java/org/burning/sport/javase/thread/reentrantlock/condition/WaxOmatic.java

线程阻塞工具类:LockSupport

  LockSupport是一个非常方便实用的线程阻塞工具,它可以在线程内任意位置让线程阻塞。和Thread.suspend()相比,它弥补了由于resume()在前发生,

  导致线程无法记录执行的情况。和Object.wait()相比,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。

  LockSupport提供的阻塞和唤醒的方法

  void park()   阻塞当前线程,如果调用 unpark(Thread thread) 方法或者当前线程被中断,才能从park方法返回

  void parkNanos(long nanos)  阻塞当前线程,最长不超过nonos纳秒,返回条件在park()的基础上增加了超时返回

  void parkUntil(long deadline)  阻塞当前线程,知道deadline时间(从1970年开始到deadline时间的毫秒数)

  void unpark(Thead thread)  唤醒处于阻塞状态的线程Thread

  LockSupport sample demo:

package org.burning.sport.javase.thread.reentrantlock.lock.support;

import java.util.concurrent.locks.LockSupport;

public class LockSupportTest {
private static Object u = new Object();
static ChangeObjectThread thread1 = new ChangeObjectThread("t1");
static ChangeObjectThread thread2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String threadName) {
super.setName(threadName);
} @Override
public void run() {
synchronized (u) {
System.out.println("in " + getName());
LockSupport.park();
}
}
}
public static void main(String[] args) throws InterruptedException {
thread1.start();
Thread.sleep(100);
thread2.start();
LockSupport.unpark(thread1);
LockSupport.unpark(thread2);
thread1.join();
thread2.join(); }
}

读写锁 ReentrantReadWriteLock

  1、使用示例:

package org.burning.sport.javase.thread.readwritelock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; public class Cache {
static Map<String, String> map = new HashMap<>();
static ReadWriteLock lock = new ReentrantReadWriteLock();
static Lock readLock = lock.readLock();
static Lock writeLock = lock.writeLock(); public static final Object get(String key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
} public static final void put(String key, String value) {
writeLock.lock();
try {
map.put(key, value);
} finally {
writeLock.unlock();
}
} public static void clear() {
writeLock.lock();
try {
map.clear();
} finally {
writeLock.unlock();
}
}
}

  2、读写锁的实现分析:

  2.1、读写状态设计

    读写锁同样依赖自定义同步器来实现同步功能。而读写状态就是其同步器的同步状态。同步状态表示锁被一个线程重复获取的次数,而读写锁的自定义同步器需要在

  同步状态(一个整形变量)上维护多个读线程和一个写线程的状态。如果在一个整形变量上维护多种状态,就一定需要 “按位切割使用” 这个变量,读写锁将变量切分为了两

  个部分,高16位表示读,低16位表示写。

  2.2、写锁的获取与释放

    写锁是一个支持重入的排它锁。如果当前线程已经获取了写锁,则增加写状态。写锁的释放与ReentrantLock的释放过程基本类似,每次释放均减少写状态,当写状态

  为0时表示写锁已被释放。

  2.3、读锁的获取与释放

    读锁是一个支持重入的共享锁,它能够被多个线程同时获取,在没有其他写线程访问(或者写状态为0)时,读锁总会被成功的获取,而所做的也只(线程安全的)是增加读

  状态。读锁的每次释放均减少读状态,减少的值是(1<<16)。

  2.4、锁降级

    锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有)写锁,

  再获取到读锁,随后释放写锁的过程。(ReentrantReadWriteLock不支持锁升级)

参考:

  【1】《Java并发编程的艺术》,方腾飞

  【2】《Java并发编程实战》,童云兰

  【3】《Java高并发程序设计》,葛一鸣

  【4】《Think In Java》,第21章 并发

    

Lock接口的更多相关文章

  1. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  2. 线程同步 Lock接口

    同步:★★★★★ 好处:解决了线程安全问题. 弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁. 定义同步是有前提的: 1,必须要有两个或者两个以上的线程,才需要同步. 2,多个线程必须保证使用 ...

  3. Java基础知识强化之多线程笔记06:Lock接口 (区别于Synchronized块)

    1. 简介 我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式 ...

  4. Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock

    在JDK5里面,提供了一个Lock接口.该接口通过底层框架的形式为设计更面向对象.可更加细粒度控制线程代码.更灵活控制线程通信提供了基础.实现Lock接口且使用得比较多的是可重入锁(Reentrant ...

  5. jdk1.5多线程Lock接口及Condition接口

    jdk1.5多线程的实现的方式: jdk1.5之前对锁的操作是隐式的 synchronized(对象) //获取锁 { } //释放锁 jdk1.5锁的操作是显示的:在包java.util.concu ...

  6. Java多线程的~~~Lock接口和ReentrantLock使用

    在多线程开发.除了synchronized这个keyword外,我们还通过Lock接口来实现这样的效果.由Lock接口来实现 这样的多线程加锁效果的优点是非常的灵活,我们不在须要对整个函数加锁,并且能 ...

  7. Java 中的锁——Lock接口

    Java SE5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能.虽然它少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的操作性. ...

  8. 5.Lock接口及其实现ReentrantLock

    jdk1.7.0_79 在java.util.concurrent.locks这个包中定义了和synchronized不一样的锁,重入锁——ReentrantLock,读写锁——ReadWriteLo ...

  9. 多线程里面的关键字,wait, notfiy, 锁(synchronized), lock接口

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  10. java多线程Lock接口简介使用与synchronized对比 多线程下篇(三)

    前面的介绍中,对于显式锁的概念进行了简单介绍 显式锁的概念,是基于JDK层面的实现,是接口,通过这个接口可以实现同步访问 而不同于synchronized关键字,他是Java的内置特性,是基于JVM的 ...

随机推荐

  1. Why is it called “armature” instead of “skeleton”? or perhaps “rig”?

    Great question, I’ve always assumed armature/skeleton to be the same thing, here’s a quote from an a ...

  2. ubuntu中给python3安装opencv

    一.安装相关工具包******注意:以下3,4,5,6为可选项,根据需求安装******1.更新库 sudo apt-get update sudo apt-get upgrade 2.安装从源码构建 ...

  3. Scrum冲刺阶段1

    各个成员在 Alpha 阶段认领的任务 人员 任务 何承华 美化设计 部分后端设计 陈宇 后端设计 丁培辉 美化设计 部分后端设计 温志铭 前端设计 杨宇潇 服务器搭建 张主强 前端设计 明日各个成员 ...

  4. REdis AOF文件结构分析

    REdis-4.0之前的AOF文件没有文件头,而从REdis-4.0开始AOF文件带有一个文件头,文件头格式和RDB文件头相同. REdis-4.0版本,如果开启aof-use-rdb-preambl ...

  5. hdu 4911 Inversion and poj2299 [树状数组+离散化]

    题目 题意:  给你一串数字,然后给你最多进行k次交换(只能交换相邻的)问交换后的最小逆序对个数是多少. 给你一个序列,每次只能交换相邻的位置,把他交换成一个递增序列所需要的最少步数 等于 整个序列的 ...

  6. 《mysql必知必会》学习_第18章_20180807_欢

    第18章 全文本搜索 P121  #创建一个新表,对表的列进行定义,定义之后,MySQL自动维护该索引# create table productnotes ( note_id  int   NOT ...

  7. Ubuntu 安装 chrome浏览器

    按下 Ctrl + Alt + t 键盘组合键,启动终端. 输入以下命令: sudo wget http://www.linuxidc.com/files/repo/google-chrome.lis ...

  8. EF db first 获取表名称

    一直以来,使用DB FIRST的方式,想得到表名,最后一直不得其法.直到昨天晚上,反编译自己的程序集的时候,突然发现EF表结构和数据实体类的映射关系存在什么地方.然后就有了这篇文章. 咱们一步步来. ...

  9. Java SE核心之一:常用类,包装类,其他基本数据类型包装类。

    在Java继承体系中,java.lang.Object类位于顶端(是所有对象的直接或间接父类).如果一个类没有写extends关键字声明其父类,则该类默认继承java.lang.Object类.Obj ...

  10. vsftpd安装配置以及常见问题解决

    vsftpd安装配置以及踩坑解决办法,Centos7 nginx已经配置成功了,但是使用http始终没办法访问到图片,那么你来对地方了(在文章末尾是原因) 配置nginx教程:http://blog. ...