上文学习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. jQuery经典面试题及答案精选[转]

    这两天有个面试,把这些记在这里. 问题:jQuery的美元符号$有什么作用? 回答:其实美元符号$只是”jQuery”的别名,它是jQuery的选择器,如下代码: $(document).ready( ...

  2. 更改gradle的java的class文件输出目录的结构

    group 'com.thinkvenus.common'version '1.0-SNAPSHOT' apply plugin: 'java' sourceCompatibility = 1.8 r ...

  3. python学习笔记(十四)之字典

    字典:是python中唯一的映射类型,字典中每一项都是由键-值对组成的项.字典中没有索引,只有键和值.键的类型可以是整型,变量或字符串. 创建和访问字典: >>> dict1 = { ...

  4. var_dump打印出来格式太乱 怎么调

    var_dump()和print_r() 输出的都是文本格式,在浏览器中就是这样如果你加载了 xdebug 扩展,那么 var_dump() 就会以 html 格式输出

  5. Python3 断言

    #!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:CarsonLi ''' 断言一般用于后面有非常重要的操作,需要使用前面的数据,而且不容许出 ...

  6. 72.xilinx vivado zynq vdma仿真及应用详解(一)

    很多人用zynq平台做视频图像开发,但是对vdma了解比较少,上手起来稍微有些困难,我针对这一现象,做了一个基于vivado和modelsim的仿真和应用测试工程,并写篇文章做些介绍,希望能对大家有帮 ...

  7. 浅析busybox如何集成到openwrt

    背景 近日添加了一个包到openwrt中,在此过程中又对openwrt多了一些认识 这个包本身自带了kconfig,可直接在这个包里面执行make menuconfig进行配置,然后执行make 但要 ...

  8. Workqueue机制的实现

    Workqueue机制中定义了两个重要的数据结构,分析如下: cpu_workqueue_struct结构.该结构将CPU和内核线程进行了绑定.在创建workqueue的过程中,Linux根据当前系统 ...

  9. 【BubbleCup X】G:Bathroom terminal

    一个hash的题 对?出现位置直接暴力枚举,然后hash判断下,扔进map里 cf的评测机跑的针tm块 #include<bits/stdc++.h> ; ; typedef long l ...

  10. Linux中如何配置IP相关文件

    Linux中如何配置IP 与网络相关的文件:1) /etc/sysconfig/network   设置主机名称及能否启动Network2) /etc/sysconfig/network-script ...