trylock 定时 java,知否知否,应知ReentrantLock和synchronized的区别

在应用开发中总是会遇到需要对方法进行加锁的场景,java中为我们提供了两种加锁方式,一是synchronized,二是lock方式。那么在实际开发中我们应该选取哪种加锁方式呢

写在前面

通过阅读本篇文章,你将了解到:

synchronized和ReentrantLock的不同点、相同点

ReentrantLock、synchronized响应中断测试

ReentrantLock#tryLock定时获取锁测试

ReentrantLock公平锁、非公平锁测试

ReentrantLock和synchronized性能测试

ReentrantLock应用场景介绍

相同点

互斥 => 同时只有一个线程获取锁

内存可见性 => 对共享变量的修改对另一个线程立即可见

可重入 => 同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞

不同点

synchronize

ReentrantLock

加锁释放锁方式

使用者无需关心,自动加锁、释放锁

显式加锁、释放锁,必须调用lock方法获取锁,调用unlock方法释放锁

中断

不可响应中断

可响应中断

超时获取锁

不允许

允许

是否可以实现公平锁

否,默认就为非公平锁

是,通过构造函数指定是否为公平锁,默认为非公平锁,传入true为公平锁

实现方式

JVM级别

API级别

ReentrantLock相比synchronized更灵活一些

ReentrantLock方法测试

ReentrantLock是Lock接口的其中一个实现类,Lock接口中定义的方法有:

void lock() => 立即获取锁

boolean tryLock() => 立即获取锁

boolean tryLock(long time, TimeUnit unit) throws InterruptedException => 在指定时间内获取锁,获得锁返回true,获取不到锁返回false

void lockInterruptibly() throws InterruptedException => 获取可中断锁

void unlock() => 释放锁

Condition newCondition() => 返回绑定到此 Lock 实例的Condition 实例

lock和tryLock方法除了返回值不一样以外,lock获取到的锁是不可响应中断的,而tryLock获取到的锁是可响应中断的。除此以外tryLock(long time, TimeUnit unit)获取到的锁也是可响应中断,即获取锁的方法中只有lock方法获取到的锁是不可以响应中断的

lockInterruptibly响应中断:ReentrantLockLockInterruptiblyTest.java

public class ReentrantLockLockInterruptiblyTest {

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(new Task());

thread.start();

Thread.sleep(3 * 1000);  //执行三秒后中断线程

thread.interrupt();

}

public static class Task implements Runnable {

Lock lock = new ReentrantLock();

public Task () {

new Thread(() -> {

try {

lockMethod();

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

}

@Override

public void run() {

try {

lockMethod();

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("End...");

}

private void lockMethod() throws InterruptedException {

lock.lockInterruptibly();

try {

//模拟长时间不释放锁

while (true) {}

} finally {

lock.unlock();

}

}

}

}

执行结果:

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)

at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)

at com.h2t.study.concurrent.lock.ReentrantLockLockInterruptiblyTest$Task.lockMethod(ReentrantLockLockInterruptiblyTest.java:45)

at com.h2t.study.concurrent.lock.ReentrantLockLockInterruptiblyTest$Task.run(ReentrantLockLockInterruptiblyTest.java:37)

at java.lang.Thread.run(Thread.java:748)

End...

中断成功

synchronize响应中断:SynchronizedBlock.java

public class SynchronizedBlock {

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(new Task());

thread.start();

Thread.sleep(3 * 1000);  //执行三秒后中断线程

thread.interrupt();

System.out.println(thread.isInterrupted());

}

public static class Task implements Runnable {

public Task() {

new Thread() {

public void run() {

f();

}

}.start();

}

public synchronized void f() {

while (true) {

}

}

@Override

public void run() {

f();

System.out.println("End");

}

}

}

控制台永远不会抛出异常、打印出End

对于synchronized来说,如果一个线程在等待锁,调用中断线程的方法,不会生效即不响应中断。而lock可以响应中断

tryLock定时锁:ReentrantLockTryLockTest.java

public class ReentrantLockTryLockTest {

public static void main(String[] args) {

ExecutorService es = Executors.newCachedThreadPool();

for (int i = 0; i < 2; i++) {

es.execute(new Task(i));

}

}

private static class Task implements Runnable {

private static Lock lock = new ReentrantLock();

private int i;

public Task(int i) {

this.i = i;

}

@Override

public void run() {

try {

lockMethod();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//每次只允许一个线程调用

private void lockMethod() throws InterruptedException {

long start = System.currentTimeMillis();

//2s内获得锁

if (lock.tryLock(2, TimeUnit.SECONDS)) {

System.out.println(String.format("i = %d 获取到锁,耗时:%d", i, System.currentTimeMillis() - start));

try {

Thread.sleep(1000 * 60 * 1);  //睡眠1分钟

} finally {

lock.unlock();

}

} else {

System.out.println(String.format("i = %d 获取到锁失败,耗时:%d", i, System.currentTimeMillis() - start));

}

}

}

}

run方法中调用了加锁的方法,加锁方法中尝试在2s内获得锁

执行结果:

i = 0 获取到锁,耗时:0

i = 1 获取到锁失败,耗时:2001

ReentrantLock公平锁与非公平锁:ReentrantLockFairTest.java

public class ReentrantLockFairTest {

//通过传入true创建一个公平锁

private static Lock fairLock = new ReentrantLock(true);

//非公平锁,默认为非公平锁

private static Lock unfairLock = new ReentrantLock();

public static void main(String[] args) {

ExecutorService unfairEs = Executors.newCachedThreadPool();

ExecutorService fairEs = Executors.newCachedThreadPool();

for (int i = 0; i < 5; i++) {

unfairEs.execute(new UnfairTask(i));

fairEs.execute(new FairTask(i));

}

}

/**

* 非公平锁任务

* */

private static class UnfairTask implements Runnable {

private int i;

public UnfairTask(int i) {

this.i = i;

}

@Override

public void run() {

unfairLock.lock();

try {

System.out.println(String.format("unfairTask i = %d is running", i));

} finally {

unfairLock.unlock();

}

}

}

/**

* 公平锁任务

* */

private static class FairTask implements Runnable {

private int i;

public FairTask(int i) {

this.i = i;

}

@Override

public void run() {

fairLock.lock();

try {

System.out.println(String.format("fairTask i = %d is running", i));

} finally {

fairLock.unlock();

}

}

}

}

执行结果:

unfairTask i = 0 is running

fairTask i = 0 is running

unfairTask i = 1 is running

fairTask i = 1 is running

unfairTask i = 2 is running

fairTask i = 3 is running

unfairTask i = 3 is running

fairTask i = 2 is running

fairTask i = 4 is running

unfairTask i = 4 is running

公平锁先到先得,因此执行顺序是有序的。非公平锁如果后来提交的线程刚好获取释放掉的锁将获得锁先执行,因此结果执行顺序是无序的

synchronized非公平锁:

public class SynchronizedUnfairTest {

public static void main(String[] args) {

ExecutorService unfairEs = Executors.newCachedThreadPool();

for (int i = 0; i < 5; i++) {

unfairEs.execute(new UnfairTask(i));

}

}

/**

* 非公平锁任务

* */

private static class UnfairTask implements Runnable {

private int i;

public UnfairTask(int i) {

this.i = i;

}

@Override

public synchronized void run() {

System.out.println(String.format("unfairTask i = %d is running", i));

}

}

}

执行结果:

unfairTask i = 1 is running

unfairTask i = 0 is running

unfairTask i = 3 is running

unfairTask i = 2 is running

unfairTask i = 4 is running

synchronized默认为非公平锁,并且只能是非公平锁,因此执行结果顺序是无序的

性能测试

ReentrantLock加锁任务:ReentrantLockTask.java

public class ReentrantLockTask implements Runnable {

private int i;

public ReentrantLockTask(int i) {

this.i = i;

}

@Override

public void run() {

lockMethod();

}

ReentrantLock lock = new ReentrantLock();

private void lockMethod() {

int sum = 0;

lock.lock();

try {

for (int j = 0; j < 10; j++) {

sum += j;

}

} finally {

lock.unlock();

}

}

}

synchronized加锁任务:SynchronizedLockTask.java

public class SynchronizedLockTask implements Runnable {

private int i;

public SynchronizedLockTask(int i) {

this.i = i;

}

@Override

public void run() {

lockMethod();

}

private synchronized void lockMethod() {

int sum = 0;

for (int j = 0; j < 10; j++) {

sum += j;

}

}

}

测试类:PerformTest

public class PerformTest {

public static void main(String[] args) {

for (int i = 100; i < 1000000000; i = i * 10) {

reentrantLockTest(i);

synchronizedLockTest(i);

}

}

/**

* 循环执行的次数

* */

private static void reentrantLockTest(int time) {

ExecutorService es = Executors.newCachedThreadPool();

long start = System.currentTimeMillis();

for (int i = 0; i < time; i++) {

es.execute(new ReentrantLockTask(i));

}

System.out.println(String.format("ReentrantLockTest time = %d Spend %d", time, System.currentTimeMillis() - start));

}

private static void synchronizedLockTest(int time) {

ExecutorService es = Executors.newCachedThreadPool();

long start = System.currentTimeMillis();

for (int i = 0; i < time; i++) {

es.execute(new SynchronizedLockTask(i));

}

System.out.println(String.format("SynchronizedLockTest time = %d Spend %d", time, System.currentTimeMillis() - start));

}

}

循环执行任务,统计循环任务的耗时

测试结果:

ReentrantLockTest time = 100 Spend 6

SynchronizedLockTest time = 100 Spend 2

ReentrantLockTest time = 1000 Spend 7

SynchronizedLockTest time = 1000 Spend 14

ReentrantLockTest time = 10000 Spend 42

SynchronizedLockTest time = 10000 Spend 29

ReentrantLockTest time = 100000 Spend 186

SynchronizedLockTest time = 100000 Spend 156

ReentrantLockTest time = 1000000 Spend 1428

SynchronizedLockTest time = 1000000 Spend 1006

ReentrantLockTest time = 10000000 Spend 9716

SynchronizedLockTest time = 10000000 Spend 9791

ReentrantLockTest time = 100000000 Spend 97928

SynchronizedLockTest time = 100000000 Spend 99804

synchronized和ReentrantLock性能相差不大,分不出谁好谁不好

synchronized和ReentrantLock之间如何选择

synchronized和ReentrantLock性能差不多,当且仅当synchronized无法满足的情景下使用ReentrantLock,因为ReentrantLock需要显式释放锁,同时synchronized是JVM级别的,JVM能对其进行优化,而Reentrant是API级别的不会有任何优化。synchronized无法满足的情景:

定时获取锁

响应中断

需要以公平的方式获取锁

最后附:示例代码

欢迎fork与star

149043cb17d5885ab3c86d58f2654bc7.png

附往期文章:欢迎你的阅读、点赞、评论

并发相关

1.为什么阿里巴巴要禁用Executors创建线程池?

2.自己的事情自己做,线程异常处理

设计模式相关:

1. 单例模式,你真的写对了吗?

2. (策略模式+工厂模式+map)套餐 Kill 项目中的switch case

JAVA8相关:

1. 使用Stream API优化代码

2. 亲,建议你使用LocalDateTime而不是Date哦

数据库相关:

1. mysql数据库时间类型datetime、bigint、timestamp的查询效率比较

2. 很高兴!终于踩到了慢查询的坑

高效相关:

1. 撸一个Java脚手架,一统团队项目结构风格

日志相关:

1. 日志框架,选择Logback Or Log4j2?

2. Logback配置文件这么写,TPS提高10倍

工程相关:

1. 闲来无事,动手写一个LRU本地缓存

2. Redis实现点赞功能模块

3. JMX可视化监控线程池

4. 权限管理 【SpringSecurity篇】

5. Spring自定义注解从入门到精通

6. java模拟登陆优酷

7. QPS这么高,那就来写个多级缓存吧

8. java使用phantomjs进行截图

其他:

1. 使用try-with-resources优雅关闭资源

2. 老板,用float存储金额为什么要扣我工资

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值