高并发编程之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. ...
随机推荐
- 【转】 虚拟机Linux不能上网简单有效的解决办法(NAT模式下)
前提条件: a. 确保VMware Network Adapter for VMnet8 处于启用状态,这个是NAT网络模式要用的虚拟网卡.注意这个网卡的IP地址,不需要做手动设置 b. 确保 VMw ...
- 简单高效的asp.net目录树源代码
前台页面: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default. ...
- Traffic-Server配置(待补充和更新)
Server 5.3.2 测试1.裸盘:remap.configmap http://192.168.227.131 http://192.168.227.131:8080 #traffic_serv ...
- cookie 跨域的问题
今天研究一天发现cookie无法设置除当前域名或者其父域名之外的其他domain. 这个是浏览器出于对cookie的保护造成的,也就是cookie无法跨域设置. 对于子域名也有如下规则,当前域名只能设 ...
- [Leetcode] Sum 系列
Sum 系列题解 Two Sum题解 题目来源:https://leetcode.com/problems/two-sum/description/ Description Given an arra ...
- linux下C语言实现的内存池【转】
转自:http://blog.chinaunix.net/uid-28458801-id-4254501.html 操作系统:ubuntu10.04 前言: 在通信过程中,无法知道将会接收到的 ...
- mysql开启GTID跳过错误的方法【转】
1.数据库版本 MySQL> select version() -> ;+-------------------------------------------+| version( ...
- 深入解析Mysql 主从同步延迟原理及解决方案
MySQL的主从同步是一个很成熟的架构,优点为:①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,可以 ...
- Loadrunner脚本学习总结
1.1 web脚本录制选择Web(HTTP/HTML)协议: 注意录制脚本前选择如下协议: 1.2 脚本如果需要使用如下函数: web_reg_save_param.web_fin ...
- POJ 2230 Watchcow(欧拉回路:输出点路径)
题目链接:http://poj.org/problem?id=2230 题目大意:给你n个点m条边,Bessie希望能走过每条边两次,且两次的方向相反,让你输出以点的形式输出路径. 解题思路:其实就是 ...