高并发编程之ReentrantLock
上文学习jvm提供的同步方法synchronized的用法,一些常见的业务类型以及一道以前阿里的面试题,从中学习到了一些并发编程的一些规则以及建议,本文主要学习jdk提供的同步方法reentrantLock。
一、ReentrantLock关键字
reentrantLock是JDK提供的一款同步手工锁,可重入锁。
reentrantLock可以完成synchronized做的同样的功能,但是需要手工释放锁,使用synchronized的时候遇到异常jvm会自动释放锁,但是reentrantLock不会自动释放,需要手动去释放锁,所以一般是将释放锁写到finally里面的。
我们看下面代码:
/**
* reentrantLock可以完成synchronized做的同样的功能,但是需要手工释放锁,
* 使用synchronized的时候遇到异常jvm会自动释放锁,但是reentrantLock不会自动释放,
* 需要手动去释放锁,所以一般是将释放锁写到finally里面的。
* @author Wuyouxin
*
*/
public class ReentrantLock1 {
Lock lock = new ReentrantLock();
void m1 (){
try {
lock.lock();//相当于synchronized(this)
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
} void m2 (){
//如果想要两个方法互斥,则锁定同一把锁即可
lock.lock();
System.out.println("m2 ...");
lock.unlock();
} public static void main(String[] args) {
final ReentrantLock1 r1 = new ReentrantLock1();
new Thread(new Runnable() { @Override
public void run() {
r1.m1();
}
}, "t1").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} new Thread(new Runnable() { @Override
public void run() {
r1.m2();
}
}, "t2").start();
}
}
二、tryLock方法
在使用reentrantLock时还可以进行尝试性锁定“tryLock”,这样会去判断是否可以锁定,或者指定时间内是否可以锁定,线程可以决定是否继续等待。
我们看下面代码:
/**
* 在使用reentrantLock时还可以进行尝试性锁定“tryLock”,这样会去判断是否可以锁定,
* 或者指定时间内是否可以锁定,线程可以决定是否继续等待。
* @author Wuyouxin
*
*/
public class ReentrantLock2 {
Lock lock = new ReentrantLock(); void m1 (){
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 使用tryLock锁定尝试锁定,不管是否锁定,方法都将继续执行
* 也可以根据tryLock的返回值来判断是否锁定
*/
void m2 (){
boolean b = lock.tryLock();
try {
if (b){
System.out.println("m2已经锁定");
//已经锁定的业务逻辑
} else {
System.out.println("m2没有锁定");
//没有锁定的业务逻辑
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (b){
lock.unlock();
}
}
} /**
* 如果在5秒内没有锁定对象则继续进行
*/
void m3 (){
boolean b = false;
try {
b = lock.tryLock(5, TimeUnit.SECONDS);
if (b){
System.out.println("m3已经锁定");
//已经锁定的业务逻辑
} else {
System.out.println("m3没有锁定");
//没有锁定的业务逻辑
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (b){
lock.unlock();
}
}
} public static void main(String[] args) {
final ReentrantLock2 r2 = new ReentrantLock2(); new Thread(new Runnable() { @Override
public void run() {
r2.m1();
}
}, "t1").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} new Thread(new Runnable() { @Override
public void run() {
r2.m2();
}
}, "t2").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} new Thread(new Runnable() { @Override
public void run() {
r2.m3();
}
}, "t3").start();
}
}
三、lockInterruptibly方法
使用ReentrantLock还可以使用lockInterruptibly方法可以对interrupt做出响应,在一个线程等待锁的过程中可以被打断。
我们看下面代码:
/**
* 使用ReentrantLock还可以使用lockInterruptibly方法可以对interrupt做出响应,
* 在一个线程等待锁的过程中可以被打断。
* @author Wuyouxin
*
*/
public class ReentrantLock3 { public static void main(String[] args) {
final Lock lock = new ReentrantLock(); new Thread(new Runnable() { @Override
public void run() {
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}, "t1").start(); Thread t2 = new Thread(new Runnable() { @Override
public void run() {
boolean b = false;
try {
lock.lockInterruptibly();
b = true;
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (InterruptedException e) {
System.out.println("Interrupt!");
e.printStackTrace();
} finally {
System.out.println("unlock");
if (b){
lock.unlock();
}
}
}
}, "t2");
t2.start(); try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();
}
}
上面代码由于t2线程在调用interrupt方法时没有获取到资源,所以由主线程可以直接打断t2线程。
四、ReentrantLock公平锁
公平锁:当一个线程池运行结束之后其他线程获得锁是公平的先等待先得到。所以叫做公平锁。
非公平锁:当一个线程池运行结束之后其他线程获得锁是随机的,所以叫非公平锁,也叫竞争锁。
ReentrantLock还可以指定为公平锁。
我们看下面代码:
**
* ReentrantLock还可以设置公平锁
* @author Wuyouxin
*
*/
public class ReentrantLock4 extends Thread{
//默认为非公平锁,true为公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public void run (){
for (int i = 0; i < 100; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得锁");
} catch (Exception e){
e.printStackTrace();
} finally{
lock.unlock();
}
}
} public static void main(String[] args) {
ReentrantLock4 r4 = new ReentrantLock4();
new Thread(r4, "t1").start();
new Thread(r4, "t2").start();
}
}
面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程和10个消费者线程的阻塞调用。
使用synchronized 的 wait 和 notify 来实现:
public class MyContainer1<T> {
private final LinkedList<T> lists = new LinkedList<T>();
private final static int MAX = 10;
private int count = 0;
public int getCount (){
return this.count;
}
public synchronized void put (T t){
//这里为什么使用while而不是if,因为如果是if,
//当线程被唤醒后,其他线程先一步put进去,if不会再继续判断,
//而是直接往下走这时再add则会超出范围
while (lists.size() == MAX){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lists.add(t);
count++;
this.notifyAll();//通知消费者线程进行消费
}
public synchronized T get(){
T t = null;
while (lists.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = lists.removeFirst();
count--;
this.notifyAll();//通知生产者线程进行生产
return t;
}
public static void main(String[] args) {
final MyContainer1<String> container = new MyContainer1<String>();
for (int i = 0; i < 10; i++) {
//消费者线程
new Thread(new Runnable() {
@Override
public void run() {
for (int j=0;j<5;j++)System.out.println(container.get());
}
}, "c消费者:" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
//生产者线程
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 25; j++) {
container.put(Thread.currentThread().getName());
}
}
}, "p生产者:" + i).start();
}
}
}
这里为什么使用while而不是if,因为如果是if,当线程被唤醒后,其他线程先一步put进去,if不会再继续判断,而是直接往下走这时再add则会超出范围。
使用Lock 和 Condition实现,可以精确的唤醒某些线程:
/**
* 使用lock和 Condition实现,Condition方法可以精确的唤醒某些线程。
* @author Wuyouxin
*
*/
public class MyContainer2<T> { final private LinkedList<T> list = new LinkedList<T>();
final private static int MAX = 10;
private int conut = 0; private Lock lock = new ReentrantLock(); private Condition producer = lock.newCondition();//生产者锁
private Condition consumer = lock.newCondition();//消费者锁 public int getCount (){
return this.conut;
} public void put (T t){
try {
lock.lock();
while (this.list.size() == MAX){
producer.await();
}
this.list.add(t);
this.conut++;
this.consumer.signalAll();//通知消费者线程开始消费
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public T get(){
T t = null;
try {
lock.lock();
while (this.list.size() == 0){
consumer.await();
}
t = this.list.removeFirst();
this.conut--;
producer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
} public static void main(String[] args) {
final MyContainer2<String> c = new MyContainer2<String>(); for (int i = 0; i < 10; i++) {
new Thread(new Runnable() { @Override
public void run() {
while(true){
System.out.println(c.get());
}
}
}, "c" + i).start();
} for (int i = 0; i < 2; i++) {
new Thread(new Runnable() { @Override
public void run() {
while(true){
c.put(Thread.currentThread().getName());
}
}
}, "p" + i).start(); }
} }
五、ThreadLocal(线程局部变量)
ThreadLocal是空间转换时间,synchronized是时间转换空间,比如Hibernate中的session就存在ThreadLocal中,避免synchronized使用。
/**
* ThreadLock线程局部变量
*
* ThreadLocal是空间转换时间,synchronized是时间转换空间,
* 比如Hibernate中的session就存在ThreadLocal中,避免synchronized使用。
* @author Wuyouxin
*
*/
public class ThreadLocal1 {
static ThreadLocal<Person> t1 = new ThreadLocal<Person>(); public static void main(String[] args) {
new Thread(new Runnable() { @Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t1.get());
}
}, "t1").start(); new Thread(new Runnable() { @Override
public void run() {
t1.set(new Person());
}
}, "t2").start();
}
} class Person {
String name = "zhangsan";
}
按理来说t1对象是同一个,第二个线程往里面set了一个对象,第一个线程应该可以get到,但是ThreadLocal不可以,他相当于线程的局部变量,不可以被其他线程获取。
高并发编程之ReentrantLock的更多相关文章
- 高并发编程之synchronized
一.什么是线程? 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程 ...
- Java并发编程之ReentrantLock源码分析
ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...
- 并发编程之J.U.C的第二篇
并发编程之J.U.C的第二篇 3.2 StampedLock 4. Semaphore Semaphore原理 5. CountdownLatch 6. CyclicBarrier 7.线程安全集合类 ...
- 并发编程之J.U.C的第一篇
并发编程之J.U.C AQS 原理 ReentrantLock 原理 1. 非公平锁实现原理 2)可重入原理 3. 可打断原理 5) 条件变量实现原理 3. 读写锁 3.1 ReentrantRead ...
- 并发编程之 Condition 源码分析
前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...
- python并发编程之asyncio协程(三)
协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...
- python并发编程之multiprocessing进程(二)
python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...
- Python进阶:并发编程之Futures
区分并发和并行 并发(Concurrency). 由于Python 的解释器并不是线程安全的,为了解决由此带来的 race condition 等问题,Python 便引入了全局解释器锁,也就是同一时 ...
- 并发编程之:CountDownLatch
大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...
随机推荐
- 【BZOJ】1951[Sdoi2010]古代猪文
[题意]给定G,N,求: $$ans=G^{\sum_{i|n}\binom{n}{i}}\ \mod\ \ p$$ 1<=N,G<=10^9,p=999911659. [算法]欧拉定理+ ...
- mybatis笔记之使用Mapper接口注解
1. mybatis支持的映射方式 mybatis支持的映射方式有基于xml的mapper.xml文件.基于java的使用Mapper接口class,简单学习一下mybatis使用接口来配置映射的方法 ...
- php遍历路径——php经典实例
php遍历路径——php经典实例 代码: <html> <head> <title>遍历目录</title> <meta charset=&quo ...
- 004_Gradle 笔记——Java构建入门
Gradle是一个通用的构建工具,通过它的构建脚本你可以构建任何你想要实现的东西,不过前提是你需要先写好构建脚本的代码.而大部分的项目,它 们的构建流程基本是一样的,我们不必为每一个工程都编写它的构建 ...
- Spring之IOC,DI,动态代理,反射
Spring框架是J2EE开发中一个使用广泛的框架,它使得dao和service层的维护更加便利.Spring框架有两个重要的特征,一个是IOC,另一个是AOP.我们在这里主要介绍IOC,以及IOC中 ...
- NTP算法
网络时间协议 由特拉华大学的David L. Mills热心提供.http://www.eecis.udel.edu/~mills mills@udel.edu 由Reinhard v. Hanxle ...
- 20165301 2017-2018-2 《Java程序设计》第八周学习总结
20165301 2017-2018-2 <Java程序设计>第八周学习总结 教材学习内容总结 第十二章:Java多线程机制 进程与线程 操作系统与进程:进程是程序的一次动态执行过程. 进 ...
- CMS(Concurrent Mark-Sweep)垃圾回收器
http://www.iteye.com/topic/1119491 1.总体介绍: CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器.对于要 ...
- Jmeter组件和属性(二)
Jmeter脚本开发原则 简单.正确.高效.简单:去除无关的组件,同时能复用的尽量复用.正确:对脚本或者业务正确性进行必要的判断,不能少也不能多.(200),业务错误的情况下,也可能返回200,必须用 ...
- 基于layui+cropper.js实现上传图片的裁剪功能
最近因项目需求,需要在上传图片的时候先对图片裁剪,然后在上传,所以就有了本文的出现. 开始正文之前,要提一下这个图片的裁剪:图片的裁剪,有前端裁剪,也可以后端裁剪 前端的裁剪我知道的可以分为这么两种: ...