上文学习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的更多相关文章

  1. 高并发编程之synchronized

    一.什么是线程? 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程 ...

  2. Java并发编程之ReentrantLock源码分析

    ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...

  3. 并发编程之J.U.C的第二篇

    并发编程之J.U.C的第二篇 3.2 StampedLock 4. Semaphore Semaphore原理 5. CountdownLatch 6. CyclicBarrier 7.线程安全集合类 ...

  4. 并发编程之J.U.C的第一篇

    并发编程之J.U.C AQS 原理 ReentrantLock 原理 1. 非公平锁实现原理 2)可重入原理 3. 可打断原理 5) 条件变量实现原理 3. 读写锁 3.1 ReentrantRead ...

  5. 并发编程之 Condition 源码分析

    前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...

  6. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  7. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  8. Python进阶:并发编程之Futures

    区分并发和并行 并发(Concurrency). 由于Python 的解释器并不是线程安全的,为了解决由此带来的 race condition 等问题,Python 便引入了全局解释器锁,也就是同一时 ...

  9. 并发编程之:CountDownLatch

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...

随机推荐

  1. 浅析 Spring Aop

    aop也是Spring里比较重要的一个点,最近把源码看了下,这里总结一下 使用上主要就下面的点注意下: 相关的Annotaion Around Before After AfterReturning ...

  2. 使用npm安装包失败的解决办法(使用npm国内镜像介绍)

    镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在): 1.通过config命令 npm config set registry https://regist ...

  3. Linux端口占用

    1.netstat netstat -anp | grep 23232 Sample: [root@BICServer 0825]# netstat -anp | grep 23232 tcp 0 0 ...

  4. linux编程之main()函数启动过程【转】

    转自:http://blog.csdn.net/gary_ygl/article/details/8506007 1 最简单的程序  1)编辑helloworld程序,$vim helloworld. ...

  5. rabbitmq和kafka怎么选?【转】

    MQ框架非常之多,今天简单说一下有代表性的两个MQ(rabbitmq和kafka).经常会有人问rabbitmq和kafka到底哪个好呢?其实没有好与不好之分,只有哪个更合适,首先要根据自己项目的业务 ...

  6. MySQL5.6 新特性之GTID【转】

    转自 MySQL5.6 新特性之GTID - jyzhou - 博客园http://www.cnblogs.com/zhoujinyi/p/4717951.html 背景: MySQL5.6在5.5的 ...

  7. 【转载】C#异常Retry通用类

    //Retry机制 public static class Retry { /// <summary> /// 重试零个参数无返回值的方法 /// </summary> /// ...

  8. Windows上安装Jekyll

    Jekyll是什么 jekyll是一个简单的免费的Blog生成工具,是一个静态站点生成器, 它会根据网页源码生成静态文件.它提供了模板.变量.插件等功能,所以实际上可以用来编写整个网站.也可使用基于j ...

  9. OC学习篇之---类的延展

    来源:http://blog.csdn.net/jiangwei0910410003/article/details/41775603 前一篇文章我们介绍了类的类目概念和使用:http://blog. ...

  10. numpy 练习

    numpy学习,为后续机器学习铺垫 参考网址 #!/usr/bin/python #coding=utf-8 #__author__='dahu' # from numpy import * impo ...