上文学习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. 重构改善既有代码设计--重构手法13:Inline Class (将类内联化)

    某个类没有做太多事情.将这个类的所有特性搬移到另一个类中,然后移除原类. 动机:Inline Class (将类内联化)正好于Extract Class (提炼类)相反.如果一个类不再承担足够责任.不 ...

  2. 简易版jquery

    最近写了一个简易版的jquery   github地址:https://github.com/jiangzhenfei/Easy-Jquery 完成的方法: 1.$('#id') 2.extend扩展 ...

  3. JS中函数void()

    <a href="javascript:void(0)">hello</a>/* * JS中函数void()的运用大体是这种新式; * void()是运算符 ...

  4. php的发展历史

    php最初就是为了快速构建一个web页面而迅速被大家广为接受的.它的好处是在代码中能内嵌html的代码,从而让程序员能再一个页面中同时写html代码和php代码就能生成一个web页面. 这篇文章用时间 ...

  5. mysqldump 逻辑备份的正确方法【转】

    1. 利用mysqldump进行逻辑备份 1)全逻辑备份: mysqldump -uxxx -p --flush-logs --delete-master-logs --all-databases & ...

  6. sql server 2008 r2 产品密钥

    数据中心版:PTTFM-X467G-P7RH2-3Q6CG-4DMYBDDT3B-8W62X-P9JD6-8MX7M-HWK38==================================== ...

  7. 网页查看源码中&lt;div&gt的含义

    代码如下: /** HTML转义 **/ String s = HtmlUtils.htmlEscape("<div>hello world</div><p&g ...

  8. linux下设置opencv环境变量

    一.安装opencv(这里不再讲述) 二.添加库路径(创建opencv.conf文件) 输入命令:vi /etc/ld.so.conf.d/opencv.conf 输入/usr/local/lib,并 ...

  9. Tornado入门资料整理

    预备知识 没学过计网的苦逼找点现成一些的东西看吧…… <Restful Web Services>,<HTTP The Definitive Guide>,各种RFC WSGI ...

  10. js复制文字

    一.原理分析 浏览器提供了 copy 命令 ,可以复制选中的内容 document.execCommand("copy") 如果是输入框,可以通过 select() 方法,选中输入 ...