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. FortiGate日志中session clash

    1.出现于:FortiGate v5.0和v5.2 2.出现原因 Session clash messages appear in the logs when a new session is cre ...

  2. Springboot学习07-数据源Druid

    Springboot学习07-数据源Druid 关键字 Druid 前言 学习笔记 正文 1-Druid是什么 Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池.插件框架和SQL解析器 ...

  3. [leetcode]65. Valid Number 有效数值

    Validate if a given string can be interpreted as a decimal number. Some examples:"0" => ...

  4. Linux系统性能监控工具:tsar 安装、配置、以及使用

    介绍 tsar 是淘宝自己开发的一个监控工具,可用于收集和汇总系统信息,例如CPU,负载,IO和应用程序信息,例如nginx,HAProxy,Squid等.结果可以存储在本地磁盘或发送到Nagios. ...

  5. JavaScript RegExp.$1

    我们不生产代码 我们只是代码的搬运工 JavaScript RegExp.$1 RegExp 是javascript中的一个内置对象.为正则表达式. RegExp.$1是RegExp的一个属性,指的是 ...

  6. 在桌面创建robotframework Ride的快捷方式启动RIDE

    安装后robotframework-ride 后,每次启动时都要在Dos命令下启动 ,下面是创建快捷方式启动操作如下: 1.进入到python的安装目录的/Scripts目录下,找到ride.py文件 ...

  7. yii框架 隐藏index.php 以及美化URL(pathinfo模式访问)

    首先我们分步骤来: 安装好 yii 以后  我们看到的url地址如下所示: http://www.3w.com/MyApp/backend/web/index.php?r=site%2Flogin 我 ...

  8. web安全之机器学习入门——1.环境搭建

    前置知识 算法和数据的辩证关系:算法和数据是机器学习解决实际问题不可或缺的两大因素.早期机器学习十分依赖特征提取,随着发展,人们发现通过增加训练数据量,让机器从大量基础特征中可以自动关联出潜在关系,自 ...

  9. 2019.02.17 spoj Query on a tree V(链分治)

    传送门 题意简述: 给你一棵nnn个黑白点的树,初始全是黑点. 现在支持给一个点换颜色或者求整颗树中离某个点最近的白点跟这个点的距离. 思路: 考虑链分治维护答案,每个链顶用一个堆来维护答案,然后对于 ...

  10. ABP框架系列之三十八:(NHibernate-Integration-NHibernate-集成)

    ASP.NET Boilerplate can work with any O/RM framework. It has built-in integration with NHibernate. T ...