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端口聚合配置

    1.端口聚合(LACP)应用场景 该功能高端设备上支持,FortiGate60D.FortiGate90D和FortiGate240D等低端型号不支持. 1.在带宽比较紧张的情况下,通过逻辑聚合可以扩 ...

  2. (转)css3实现load效果

    本文转自:https://www.cnblogs.com/jr1993/p/4625628.html 谢谢作者 注:gif图片动画有些卡顿,非实际效果! 第一种效果: 代码如下: <div cl ...

  3. [leetcode]50. Pow(x, n)求幂

    Implement pow(x, n), which calculates x raised to the power n (xn). Example 1: Input: 2.00000, 10 Ou ...

  4. 50-2018 蓝桥杯省赛 B 组模拟赛(五)

    1.结果填空:矩阵求和 import java.math.BigInteger; import java.util.HashSet; public class Main{ public static ...

  5. Swift 模型属性

    1 .  // 定义模型属性时,一般定义为可选的,可以简化代码,不需要写 init 方法    // 如果是基本数据类型,不能设置为可选的,而且要设置初始值 var name: String? pri ...

  6. ie每次登陆出现:Windows安全性 iexplore.exe 正在连接到 记住我的凭证不起作用

    解决方案: ie浏览器--设置--Intenet选项--安全--Internet--自定义级别--用户身份验证--登陆 勾选自动使用当前用户名和密码登陆 确定--确定

  7. dos批处理(bat)运行exe

    @echo off SETLOCAL ENABLEDELAYEDEXPANSIONREM 延迟环境变量扩展 color E echo operate:1.start启动 2.stop停止 3.exit ...

  8. android 获取文本框回车输入

    扫描头开启,并发送回车 txtUsername.setOnEditorActionListener(new OnEditorActionListener() { @Override public bo ...

  9. select和其元素options

    普通的select形式为: <select> <option>选中元素1</option> <option>选中元素2</option> & ...

  10. request接受表单数据中文乱码问题分析

    这个问题困扰了我很久,今天就来探索探索. [页面乱码] 浏览器的默认编码格式和你的jsp中的编码格式不统一造成的.假如你的jsp的头编码设置为utf-8,但是浏览器设置的是gbk,就会乱码. [pos ...