public class TestLockInterruptibly {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
doPrint("thread 1 get lock.");
do123();
doPrint("thread 1 end.");
} catch (InterruptedException e) {
doPrint("thread 1 is interrupted.");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
doPrint("thread 2 get lock.");
do123();
doPrint("thread 2 end.");
} catch (InterruptedException e) {
e.printStackTrace();
doPrint("thread 2 is interrupted!!!");
}
}
});
thread1.setName("thread 1");
thread2.setName("thread 2");
thread1.start();
try {
Thread.sleep(1000);// 等待一会使得thread1会在thread2前面执行
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1秒后把线程2中断
thread2.interrupt();
}
private static void do123() throws InterruptedException {
lock.lockInterruptibly();//等待锁的过程中会立即响应中断
doPrint(Thread.currentThread().getName() + " is locked.");
try {
doPrint(Thread.currentThread().getName() + " doSoming1....");
Thread.sleep(10000);// 等待几秒方便查看线程的先后顺序
doPrint(Thread.currentThread().getName() + " doSoming2....");
doPrint(Thread.currentThread().getName() + " is finished.");
} finally {
lock.unlock();
}
}
private static void doPrint(String text) {
System.out.println((new Date()).toLocaleString() + " : " + text);
}
}
2018-8-7 16:16:21 : thread 1 doSoming1....
2018-8-7 16:16:22 : thread 2 get lock.
java.lang.InterruptedException
2018-8-7 16:16:23 : thread 2 is interrupted!!!
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at com.thinkgem.jeesite.test.TestLockInterruptibly.do123(TestLockInterruptibly.java:60)
at com.thinkgem.jeesite.test.TestLockInterruptibly.access$1(TestLockInterruptibly.java:59)
at com.thinkgem.jeesite.test.TestLockInterruptibly$2.run(TestLockInterruptibly.java:31)
at java.lang.Thread.run(Thread.java:722)
2018-8-7 16:16:31 : thread 1 doSoming2....
2018-8-7 16:16:31 : thread 1 is finished.
2018-8-7 16:16:31 : thread 1 end.
在中断点才会响应中断,比如sleep()
public class TestLockInterruptibly {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
doPrint("thread 1 get lock.");
do123();
doPrint("thread 1 end.");
} catch (InterruptedException e) {
doPrint("thread 1 is interrupted.");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
doPrint("thread 2 get lock.");
//do123();
do321();
doPrint("thread 2 end.");
} catch (InterruptedException e) {
e.printStackTrace();
doPrint("thread 2 is interrupted!!!");
}
}
});
thread1.setName("thread 1");
thread2.setName("thread 2");
thread1.start();
try {
Thread.sleep(1000);// 等待一会使得thread1会在thread2前面执行
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1秒后把线程2中断
thread2.interrupt();
}
private static void do123() throws InterruptedException {
lock.lockInterruptibly();
doPrint(Thread.currentThread().getName() + " is locked.");
try {
doPrint(Thread.currentThread().getName() + " doSoming1....");
Thread.sleep(10000);// 等待几秒方便查看线程的先后顺序
doPrint(Thread.currentThread().getName() + " doSoming2....");
doPrint(Thread.currentThread().getName() + " is finished.");
} finally {
lock.unlock();
}
}
private static void do321() throws InterruptedException {
lock.lock();
doPrint(Thread.currentThread().getName() + " is locked.");
try {
doPrint(Thread.currentThread().getName() + " doSoming1....");
Thread.sleep(10000);// 此时才会响应中断
doPrint(Thread.currentThread().getName() + " doSoming2....");
doPrint(Thread.currentThread().getName() + " is finished.");
} finally {
lock.unlock();
}
}
private static void doPrint(String text) {
System.out.println((new Date()).toLocaleString() + " : " + text);
}
}
2018-8-7 16:23:08 : thread 1 get lock.
2018-8-7 16:23:08 : thread 1 is locked.
2018-8-7 16:23:08 : thread 1 doSoming1....
2018-8-7 16:23:09 : thread 2 get lock.
2018-8-7 16:23:18 : thread 1 doSoming2....
2018-8-7 16:23:18 : thread 1 is finished.
2018-8-7 16:23:18 : thread 1 end.
2018-8-7 16:23:18 : thread 2 is locked.
2018-8-7 16:23:18 : thread 2 doSoming1....
java.lang.InterruptedException: sleep interrupted
2018-8-7 16:23:18 : thread 2 is interrupted!!!
at java.lang.Thread.sleep(Native Method)
at com.thinkgem.jeesite.test.TestLockInterruptibly.do321(TestLockInterruptibly.java:79)
at com.thinkgem.jeesite.test.TestLockInterruptibly.access$2(TestLockInterruptibly.java:74)
at com.thinkgem.jeesite.test.TestLockInterruptibly$2.run(TestLockInterruptibly.java:32)
at java.lang.Thread.run(Thread.java:722)
已经收到中断请求,但是还是拿到锁,doSoming1...,sleep 才中断
四种情况:修饰代码块,修饰了方法,修饰了静态方法,修饰BaseClass的class对象。那这几种情况会有什么不同呢?
修饰代码块
这种情况下我们创建了一个对象lock,在代码中使用synchronized(lock)这种形式,它的意思是使用lock这个对象的内置锁。这种情况下就将锁的控制交给了一个对象。当然这种情况还有一种方式:
public
void
do
() {
synchronized
(
this
) {
System.out.println(
"is base"
);
}
}
使用this的意思就是当前对象的锁。这里也道出了内置锁的关键,我提供一把锁来保护这块代码,无论哪个线程来都面对同一把锁咯。
修饰方法
synchronized 修饰方法时锁定的是调用该方法的对象,谁调的这个同步方法,我锁定谁
package com.thinkgem.jeesite.test;
public class Test implements Runnable{
private int b = 100;
public static void main(String[] args) throws InterruptedException {
Test task = new Test();
Thread thread = new Thread(task);
thread.setName("thread-1");
thread.start();
task.m2();
System.out.println("main thread b="+task.b);
}
synchronized void m1() throws InterruptedException{
b=1000;
Thread.sleep(500);
System.out.println("b="+b);
}
synchronized void m2() throws InterruptedException{
Thread.sleep(250);
b=2000;
}
@Override
public void run() {
try {
m1();
} catch (Exception e) {
// TODO: handle exception
}
}
}
输出:
main thread b=1000
b=1000
主线程调用thread-1线程,thread-1调用m1()方法,这时thread-1拿到了thread-1的内置锁,锁定了同步代码块m1
task.m2()是当前类调用m2,拿到Test类的内置锁,锁定了同步代码块m2
不是同一个锁,所以不能互斥,各执行各的
以上分析错误,m1()是this.m1(),this是该函数所属类的当前对象,其实就是task,所以是拿到task的内置锁,m1和m2是同一个内置锁
修饰静态方法
静态方法难道有啥不一样吗?确实是不一样的,此时获取的锁已经不是this了,而this对象指向的class,也就是类锁。因为Java中的类信息会加载到方法常量区,全局是唯一的。这其实就提供了一种全局的锁。
修饰类的Class对象
这种情况其实和修改静态方法时比较类似,只不过还是一个道理这种方式可以提供更灵活的控制粒度。
java.util.concurrent.locks.Lock
前面看了synchronized,大部分的情况下差不多就够啦,但是现在系统在并发编程中复杂性是越来越高,所以总是有许多场景synchronized处理起来会比较费劲。或者像
ReentrantLock
ReentrantLock就是可重入锁,连名字都这么显式。ReentrantLock提供了和synchronized类似的语义,但是ReentrantLock必须显式的调用,比如:
public
class
BaseClass {
private
Lock lock =
new
ReentrantLock();
public
void
do
() {
lock.lock();
try
{
//....
}
finally
{
lock.unlock();
}
}
}
这种方式对于代码阅读来说还是比较清楚的,只不过有个问题,就是如果忘了加try finally或忘 了写lock.unlock()的话导致锁没释放,很有可能导致一些死锁的情况,synchronized就没有这个风险。