多线程总结-同步之ReentrantLock
1 ReentrantLock与synchronized对比
ReentrantLock与synchronized都是为了同步加锁,但ReentrantLock相对效率比synchronized高,量级较轻。
synchronized在JDK1.5版本开始,尝试优化。到JDK1.7版本后,优化效率已经非常好了。在绝对效率上,不比reentrantLock差多少。使用ReentrantLock,必须手工释放锁标记。一般都是在finally代码块中定义释放锁标记的unlock方法。
2.示例用法
2.1 基本用法
lock()与unlock()就像synchronized同步代码块的开始与结束,使用ReentrantLocky一定要记得unlock()解锁
package com.bernardlowe.concurrent.t03;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test_01 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock(); // 加锁
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock(); // 解锁
}
}
void m2(){
lock.lock();
System.out.println("m2() method");
lock.unlock();
}
public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
2.2 尝试锁
尝试锁,顾名思义是尝试获取锁标记trylock(),有两种方式
无参尝试锁:会根据是否能获取当前锁标记返回对应值
boolean tryLock();
有参阻塞尝试锁, 阻塞尝试锁,阻塞参数代表等待超时时间。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 尝试锁
*/
package com.bernardlowe.concurrent.t03;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test_02 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock();
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
void m2(){
boolean isLocked = false;
try{
// 尝试锁, 如果有锁,无法获取锁标记,返回false。
// 如果获取锁标记,返回true
// isLocked = lock.tryLock();
// 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。
// 如果超时,不等待。直接返回。
isLocked = lock.tryLock(5, TimeUnit.SECONDS);
if(isLocked){
System.out.println("m2() method synchronized");
}else{
System.out.println("m2() method unsynchronized");
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(isLocked){
// 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。
// 如果当前线程没有获取到锁标记,会抛出异常。
lock.unlock();
}
}
}
public static void main(String[] args) {
final Test_02 t = new Test_02();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
2.3 可打断
先解释下线程的几种状态:
阻塞状态: 包括普通阻塞,等待队列,锁池队列。
普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。
等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断
锁池队列: 无法获取锁标记。不是所有的锁池队列都可被打断
- 使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
- 使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。
示例代码
主线程启动了两个线程t1,t2,t1调用m1(),t2调用m2()
当主线程调用interrupt()方法,可以打断t2线程的阻塞等待锁,抛出异常
package com.bernardlowe.concurrent.t03;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test_03 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock();
for(int i = 0; i < 5; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
void m2(){
try{
lock.lockInterruptibly(); // 可尝试打断,阻塞等待锁。可以被其他的线程打断阻塞状态
System.out.println("m2() method");
}catch(InterruptedException e){
System.out.println("m2() method interrupted");
}finally{
try{
lock.unlock();
}catch(Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final Test_03 t = new Test_03();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.interrupt();// 打断线程休眠。非正常结束阻塞状态的线程,都会抛出异常。
}
}
结果如图
2.4 公平锁
操作系统cpu,为了保证效率,线程的执行机制是竞争机制,或者说是随机机制,是不公平的,使用ReentrantLock实现公平锁,是非常简单的,只需要在创建ReentrantLock的时候传一个参数ReentrantLock lock = new ReentrantLock(true);
示例代码:
TestReentrantlock是公平锁
TestSync是非公平锁
/**
* 公平锁
*/
package com.bernardlowe.concurrent.t03;
import java.util.concurrent.locks.ReentrantLock;
public class Test_04 {
public static void main(String[] args) {
TestReentrantlock t = new TestReentrantlock();
//TestSync t = new TestSync();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
class TestReentrantlock extends Thread{
// 定义一个公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public void run(){
for(int i = 0; i < 5; i++){
lock.lock();
try{
System.out.println(Thread.currentThread().getName() + " get lock");
}finally{
lock.unlock();
}
}
}
}
class TestSync extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
}
}
}
}
公平锁结果:
非公平锁结果:
多线程总结-同步之ReentrantLock的更多相关文章
- JAVA - 多线程的同步
多线程的同步 1. 锁对象. 应用场景:当某个数据可能被其他线程修改时,给涉及到数据的方法上锁,保证同一时刻只有拥有这个锁的线程能访问该数据,其他要调用这个方法的线程被阻塞.注意:必须是不同线程访问同 ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java多线程的同步控制记录
Java多线程的同步控制记录 一.重入锁 重入锁完全可以代替 synchronized 关键字.在JDK 1.5 早期版本,重入锁的性能优于 synchronized.JDK 1.6 开始,对于 sy ...
- Java多线程之同步集合和并发集合
Java多线程之同步集合和并发集合 不管是同步集合还是并发集合他们都支持线程安全,他们之间主要的区别体现在性能和可扩展性,还有他们如何实现的线程安全. 同步集合类 Hashtable Vector 同 ...
- Java基础知识笔记(五:多线程的同步问题)
编写多线程程序往往是为了提高资源的利用率,或者提高程序的运行效率,或者更好地监控程序的运行过程等.多线程同步处理的目的是为了让多个线程协调地并发工作.对多线程进行同步处理可以通过同步方法和同步语句块实 ...
- 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥) 介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...
- Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...
- Python标准库08 多线程与同步 (threading包)
Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种 ...
- 【转】【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载
原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/15334159 cocos2d-x中和Android,Windows都 一样, ...
随机推荐
- List集合去重方式及效率对比
List集合相信大家在开发过程中几乎都会用到.有时候难免会遇到集合里的数据是重复的,需要进行去除.然而,去重方式有好几种方式,你用的是哪种方式呢?去重方式效率是否是最高效.最优的呢?今天就给大家讲解一 ...
- spring-mvc+freemarker整合(sonne_game网站开发03)
今天的任务就是在spring+mybatis+springmvc的基础上,将freemarker整合进来. freemarker是什么? freemarker是一种模板引擎.它的目的是基于模板和数据, ...
- 纯CSS3创意loading文字特效
快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http: ...
- Google C++测试框架系列入门篇:第二章 开始一个新项目
上一篇:Google C++测试框架系列入门篇:第一章 介绍:为什么使用GTest? 原始链接:Setting up a New Test Project 词汇表 版本号:v_0.1 开始一个新项目 ...
- 请你讲一讲JavaScript有哪些数据类型, 数据类型判断有哪些方法?
js的8种数据类型 JavaScript中有8种数据类型, 包括基本数据类型(number, string, boolean, null, undefined, symbol, bigint)和引用数 ...
- 函数式编程里的Materialization应该翻译成什么?
Materialization是函数式编程里的一个专业术语, 用于特指函数式编程中查询被实际执行并生成结果的这一过程. 首先, 搜了一下中文资料, 暂时没有对该词的中文翻译, CSDN\博客园\阿里 ...
- 304902阿里巴巴Java开发手册1.4.0
转自官网 前言 <阿里巴巴Java开发手册>是阿里巴巴集团技术团队的集体智慧结晶和经验总结,经历了多次大规模一线实战的检验及不断完善,系统化地整理成册,回馈给广大开发者.现代软件行业的高速 ...
- 容器化之Docker小知识普及
Docker确实是个好东西,相当于一个小型虚拟机,里面环境隔离只要有对应的镜像就能运行业务应用.运行基础应用.能跑数据库等等,还能跑linux等等. Docker 从狭义上来讲就是一个进程,从广义上来 ...
- jvm(4)---垃圾回收(哪些对象可以被回收)
1.java堆中几乎放着所有对象的实例,那么什么样子的对象才是可以被回收的呢? 1.1.引用计数法: 给对象添加一个引用计数器,当有地方引用的时候,计数器就+1,引用失效就-1:任何时候当计数器为0, ...
- Web框架之Django重要组件(Django中间件、csrf跨站请求伪造)
Web框架之Django_09 重要组件(Django中间件.csrf跨站请求伪造) 摘要 Django中间件 csrf跨站请求伪造 一.Django中间件: 什么是中间件? 官方的说法:中间件是 ...