threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t::m, "thread" + i));
}
threads.forEach(Thread::start);
threads.forEach((o) -> {
try {
//join()方法阻塞调用此方法的线程,直到线程t完成,此线程再继续。通常用于在main()主线程内,等待其它线程完成再结束main()主线程。
o.join(); //相当于在main线程中同步o线程,o执行完了,main线程才有执行的机会
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
```
运行结果
期望结果是100,但是实际运行结果可能是101
```
101
```
## synchronized优化 同步代码块中的语句越少越好
```java
package demo17;
import java.util.concurrent.TimeUnit;
/**
* synchronized优化
* 同步代码块中的语句越少越好
*/
public class T {
int count = 0;
synchronized void ml() {
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁
count++;
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void m2() {
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁
//采用细粒度的锁,可以使线程争用时间变短,从而提高效率
synchronized (this) {
count++;
}
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
```
## 锁是锁在堆内存的对象上,而不是锁在栈内存的引用
```java
package demo18;
import java.util.concurrent.TimeUnit;
/**
* 锁定某个对象o,如果o的属性发生改变,不影响使用.
* 但是如果o变成另外一个对象,则锁定的对象发生改变.
* 应该避免将锁定对象的引用变成另外对象
*/
public class T {
Object o = new Object();
void m() {
synchronized (o) {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
T t = new T();
//启动线程
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//创建第二个线程
Thread t2 = new Thread(t::m, "t2");
//锁定对象发生变化,所以t2线程得以进行,如注释掉这句话,线程2将永远得不到执行机会
//锁是锁在堆内存 不是锁在栈内存
t.o = new Object();
t2.start();
}
}
```
运行结果
```
t1
t1
t1
t2
t1
t2
t1
t2
t1
```
## 不要以字符串常量作为锁定对象
```java
package demo19;
/**
* 不要以字符串常量作为锁定对象
* 在下面m1 m2 其实锁定的是同一个对象
* 这种情况下还会发生比较诡异的现象,比如你用到了一个类库,在该类库中的代码锁定了"Hello",
* 但是你读不到源码,所以你在自己的代码中锁定了"Hello",这时候有可能发生非常诡异的死锁阻塞,
* 因为你的程序和你用到的类库不经意间使用了同一把锁.
*/
public class T {
String s1 = "Hello";
String s2 = "Hello";
void m1() {
synchronized (s1) {
while (true) {
System.out.println("m1");
}
}
}
void m2() {
synchronized (s2) {
while (true) {
System.out.println("m2");
}
}
}
public static void main(String[] args) {
T t = new T();
new Thread(t::m1).start();
new Thread(t::m2).start();
}
}
```
运行结果
```
m1
m1
m1
m1
m1
m1
m1
m1
m1
m1
```
只有调用m1方法的线程能够执行。
## 模拟死锁
```java
package demo20;
public class T {
private Object o1 = new Object();
private Object o2 = new Object();
public void m1() {
synchronized (o1) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("如果出现这句话表示没有死锁");
}
}
}
public void m2() {
synchronized(o2) {
synchronized (o1) {
System.out.println("如果出现这句话表示没有死锁");
}
}
}
public static void main(String[] args) {
T t=new T();
new Thread(t::m1).start();
new Thread(t::m2).start();
}
}
```
## wait和notify
- 实现一个容器,提供两个方法,add,size
- 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
```java
package demo21;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 曾经的面试题
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能?
*/
public class MyContainer1 {
List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer1 myContainer = new MyContainer1();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
myContainer.add(new Object());
System.out.println("add " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
while (true) {
if (myContainer.size() == 5) {
break;
}
}
System.out.println("t2 结束");
}, "t2").start();
}
}
```
运行结果
```
add 0
add 1
add 2
add 3
add 4
add 5
add 6
add 7
add 8
add 9
```
我们可以发现在size等于5的时候t2并没有结束,原因就因为没有加volatile关键字,我们需要添加volatile关键字,保证t2线程能够得到通知。
```java
package demo21;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 曾经的面试题
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能?
*
* 虽然加了volatile关键字,t2线程能够接收到通知。但是上面这个代码还是存在两个问题。
* 第一个就是同步的问题,在判断条件`if (myContainer.size() == 5)`这里可能存在其他线程争用的情况,判断加上break不是一个原子操作。
* 有可能一个线程判断size已经等于5,准备要break跳出的时候,有其他线程添加了新元素,导致最终break出来之后size为6。
*
* t2线程的死循环`while (true)`很浪费CPU,如果不用死循环应该怎么做?
*/
public class MyContainer2 {
volatile List lists = new ArrayList(); //添加volatile关键字,保证t2线程能够得到通知
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer2 myContainer = new MyContainer2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
myContainer.add(new Object());
System.out.println("add " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
while (true) {
if (myContainer.size() == 5) {
break;
}
}
System.out.println("t2 结束");
}, "t2").start();
}
}
```
运行结果
```
add 0
add 1
add 2
add 3
add 4
t2 结束
add 5
add 6
add 7
add 8
add 9
```
虽然加了volatile关键字,t2线程能够接收到通知。但是上面这个代码还是存在两个问题。
- 第一个就是同步的问题,在判断条件`if (myContainer.size() == 5)`这里可能存在其他线程争用的情况,判断加上break不是一个原子操作。有可能一个线程判断size已经等于5,准备要break跳出的时候,有其他线程添加了新元素,导致最终break出来之后size为6。
- t2线程的死循环`while (true)`很浪费CPU,如果不用死循环应该怎么做?
```java
package demo21;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 曾经的面试题
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能?
*
* 虽然加了volatile关键字,t2线程能够接收到通知。但是上面这个代码还是存在两个问题。
* 第一个就是同步的问题,在判断条件`if (myContainer.size() == 5)`这里可能存在其他线程争用的情况,判断加上break不是一个原子操作。
* 有可能一个线程判断size已经等于5,准备要break跳出的时候,有其他线程添加了新元素,导致最终break出来之后size为6。
*
* t2线程的死循环`while (true)`很浪费CPU,如果不用死循环应该怎么做?
*
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以。
*
*
* 阅读下面的程序,并分析输出的结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
*
* 当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
* 当 notify/notifyAll() 被执行时,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,
* 直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
*
*
* 这里并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出的原因是
* 当t2线程的中判断size不等于5,调用wait方法,等待被t1叫醒,并且释放当前的lock对象的锁。
* 然后t1线程在执行中判断size等于5了,就会调用notify方法,但是notify方法并不会释放锁。线程t2虽然被线程t1叫醒了,但是t2线程无法获取到lock对象的锁,所以无法往下执行。
* 只有等到t1结束之后,也就是执行完synchronized 代码块的代码才释放了锁。这个时候线程t2才能获取到锁并往下执行。
*/
public class MyContainer3 {
volatile List lists = new ArrayList(); //添加volatile关键字,保证t2线程能够得到通知
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer3 myContainer = new MyContainer3();
final Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("t2 启动");
if (myContainer.size() != 5) {
try {
lock.wait(); //size不等于5时,就一直在那等着,直到被t1叫醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("t1 启动");
synchronized (lock) {
for (int i = 0; i < 10; i++) {
myContainer.add(new Object());
System.out.println("add " + i);
if (myContainer.size() == 5) {
lock.notify(); //唤醒等待的t2线程
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start();
}
}
```
运行结果
```
t2 启动
t1 启动
add 0
add 1
add 2
add 3
add 4
add 5
add 6
add 7
add 8
add 9
t2 结束
```
输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出,想想这是为什么?
原因是:当t2线程的中判断size不等于5,调用wait方法,等待被t1线程叫醒,并且释放当前的lock对象的锁。然后t1线程获取到锁,并在执行到判断size等于5了,就会调用notify方法去唤醒等待的线程t2,但是notify方法并不会释放锁。线程t2虽然被线程t1叫醒了,但是t2线程无法获取到lock对象的锁,所以无法往下执行。
只有等到t1结束之后,也就是执行完synchronized代码块的代码才释放了锁。这个时候线程t2才能获取到锁并往下执行。
解决方式如下:
```java
package demo21;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 曾经的面试题
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能?
*
* 虽然加了volatile关键字,t2线程能够接收到通知。但是上面这个代码还是存在两个问题。
* 第一个就是同步的问题,在判断条件`if (myContainer.size() == 5)`这里可能存在其他线程争用的情况,判断加上break不是一个原子操作。
* 有可能一个线程判断size已经等于5,准备要break跳出的时候,有其他线程添加了新元素,导致最终break出来之后size为6。
*
* t2线程的死循环`while (true)`很浪费CPU,如果不用死循环应该怎么做?
*
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以。
*
*
* 阅读下面的程序,并分析输出的结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
*
* 当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
* 当 notify/notifyAll() 被执行时,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,
* 直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
*
*
* 这里并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出的原因是
* 当t2线程的中判断size不等于5,调用wait方法,等待被t1叫醒,并且释放当前的lock对象的锁。
* 然后t1线程在执行中判断size等于5了,就会调用notify方法,但是notify方法并不会释放锁。线程t2虽然被线程t1叫醒了,但是t2线程无法获取到lock对象的锁,所以无法往下执行。
* 只有等到t1结束之后,也就是执行完synchronized 代码块的代码才释放了锁。这个时候线程t2才能获取到锁并往下执行。
*
*
* 解决方式如下
*/
public class MyContainer4 {
volatile List lists = new ArrayList(); //添加volatile关键字,保证t2线程能够得到通知
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer4 myContainer = new MyContainer4();
final Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("t2 启动");
if (myContainer.size() != 5) {
try {
lock.wait(); //size不等于5时,就一直在那等着,直到被t1叫醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
lock.notify(); //通知线程t1继续执行
}
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("t1 启动");
synchronized (lock) {
for (int i = 0; i < 10; i++) {
myContainer.add(new Object());
System.out.println("add " + i);
if (myContainer.size() == 5) {
lock.notify(); //唤醒等待的t2线程,本线程继续执行,直至synchronized包裹的代码块结束或者调用了wait
try {
lock.wait(); //释放锁,让t2线程获取锁,让t2得以执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start();
}
}
```
运行结果
```
t2 启动
t1 启动
add 0
add 1
add 2
add 3
add 4
t2 结束
add 5
add 6
add 7
add 8
add 9
```
## CountDownLatch
上述使用wait和notify也解决了问题,但是有点过于复杂。java提供了门闩。
```java
package demo21;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 曾经的面试题
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1 添加10个元素容器中,线程2 实现监控元素的个数,当个数到5时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能?
*
* 虽然加了volatile关键字,t2线程能够接收到通知。但是上面这个代码还是存在两个问题。
* 第一个就是同步的问题,在判断条件`if (myContainer.size() == 5)`这里可能存在其他线程争用的情况,判断加上break不是一个原子操作。
* 有可能一个线程判断size已经等于5,准备要break跳出的时候,有其他线程添加了新元素,导致最终break出来之后size为6。
*
* t2线程的死循环`while (true)`很浪费CPU,如果不用死循环应该怎么做?
*
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以。
*
*
* 阅读下面的程序,并分析输出的结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
*
* 当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
* 当 notify/notifyAll() 被执行时,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,
* 直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
*
*
* 这里并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出的原因是
* 当t2线程的中判断size不等于5,调用wait方法,等待被t1叫醒,并且释放当前的lock对象的锁。
* 然后t1线程在执行中判断size等于5了,就会调用notify方法,但是notify方法并不会释放锁。线程t2虽然被线程t1叫醒了,但是t2线程无法获取到lock对象的锁,所以无法往下执行。
* 只有等到t1结束之后,也就是执行完synchronized 代码块的代码才释放了锁。这个时候线程t2才能获取到锁并往下执行。
*
*
* notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行整个通信过程比较繁琐
* 使用Latch(门闩)替代wait notify来进行通知
* 好处是通信方式简单,同时也可以指定等待时间
* 使用await和countdown方法替代wait和notify
* CountDownLatch不涉及锁定,当count的值为0时当前线程继续运行
* 当不涉及同步,只是涉及线程通信的时候,用synchronized+wait/notify就显得太重了
* 这时应该考虑countdownlatch/cyclicbarrier/semaphore
*/
public class MyContainer5 {
volatile List lists = new ArrayList(); //添加volatile关键字,保证t2线程能够得到通知
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer5 myContainer = new MyContainer5();
CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
System.out.println("t2 启动");
if (myContainer.size() != 5) {
try {
latch.await();
//也可以指定等待时间
//latch.await(5000,TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("t1 启动");
for (int i = 0; i < 10; i++) {
myContainer.add(new Object());
System.out.println("add " + i);
if (myContainer.size() == 5) {
//打开门闩,让t2得以执行
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("t1结束");
}, "t1").start();
}
}
```
运行结果
```
t2 启动
t1 启动
add 0
add 1
add 2
add 3
add 4
t2 结束
add 5
add 6
add 7
add 8
add 9
t1结束
```
## ReentrantLock可以用来代替synchronized
ReentrantLock必须要手动释放锁。使用synchronized锁定如果遇到异常,jvm会自动释放锁,但是Lock必须手动释放,因此常常在finally中释放锁
```java
package demo22;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/* ReentrantLock用来替代synchronized
* ReentrantLock必须要手动释放锁。使用synchronized锁定如果遇到异常,jvm会自动释放锁,但是Lock必须手动释放,因此常常在finally中释放锁*/
public class T {
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) {
T t = new T();
new Thread(t::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(t::m2).start();
}
}
```
运行结果
```
0
1
2
3
4
5
6
7
8
9
m2()...
```
## ReentrantLock可以进行尝试锁定tryLock()
- 不指定时间
```java
package demo23;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用ReentrantLock可以进行尝试锁定tryLock();若无法锁定或在指定时间内无法锁定,线程可以决定是否等待
*/
public class T1 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.print(" " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将会继续执行,可以根据tryLock的返回值判定是否被锁定了
* 可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放到finally中。
*/
void m2() {
boolean locked = lock.tryLock();
System.out.print(" m2..." + locked + " ");
if (locked) lock.unlock(); //false 不指定尝试时间
}
public static void main(String[] args) {
T1 t = new T1();
new Thread(t::m1).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(t::m2).start();
}
}
```
运行结果
```
0 m2...false 1 2 3 4 5 6 7 8 9
```
- 指定时间
```java
package demo23;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用ReentrantLock可以进行尝试锁定tryLock();若无法锁定或在指定时间内无法锁定,线程可以决定是否等待
*/
public class T2 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.print(" " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将会继续执行,可以根据tryLock的返回值判定是否被锁定了
* 可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放到finally中。
*/
void m2() {
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS); //指定超时时间为5s
System.out.println(" m2..." + locked + " ");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (locked) {
lock.unlock();
}
}
}
public static void main(String[] args) {
T2 t = new T2();
new Thread(t::m1).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(t::m2).start();
}
}
```
运行结果
```
0 1 2 3 4 5 m2...false
6 7 8 9
```
## ReentrantLock的lockInterruptibly方法
ReentrantLock可调用lockInterruptibly方法,对线程的interrupt方法作出响应,在一个线程等待的过程中,可以被打断。
```java
package demo24;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*ReentrantLock可调用lockInterruptibly()方法,对线程的interrupt()方法作出响应,在一个线程等待的过程中,可以被打断。
* ReentrantLock的lock()方法是不能被打断的,即锁用lock()方法锁定,线程调用interrupt()方法是毫无作用的*/
public class T {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
System.out.print(" t1 start... ");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); //t1不停的运行,睡死了
System.out.print(" t1 end... ");
} catch (InterruptedException e) {
System.out.print(" t1-interrupted! ");
} finally {
System.out.println("t1 解锁");
lock.unlock();
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
lock.lock(); //不能对interrupt()方法作出响应
// lock.lockInterruptibly(); //也是上锁,但是可以对interrupt()方法作出响应
System.out.print(" t2 start... ");
TimeUnit.SECONDS.sleep(5);
System.out.print(" t2 end... ");
} catch (InterruptedException e) {
System.out.println(" t2-interrupted! ");
} finally {
try {
System.out.println("t2 解锁");
lock.unlock();
} catch (Exception e) {
System.out.println("没有得到锁的线程运行结束");
}
}
}, "t2");
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); //打断t2的等待,如果使用的是t2使用的是lock()方法进行上锁就无法打断,如果使用lockInterruptibl()方法就可以打断
}
}
```
运行结果
```
t1 start...
```
无法打断t2线程。
改用lockInterruptibl()
```java
package demo24;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*ReentrantLock可调用lockInterruptibly()方法,对线程的interrupt()方法作出响应,在一个线程等待的过程中,可以被打断。
* ReentrantLock的lock()方法是不能被打断的,即锁用lock()方法锁定,线程调用interrupt()方法是毫无作用的*/
public class T {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
System.out.print(" t1 start... ");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); //t1不停的运行,睡死了
System.out.print(" t1 end... ");
} catch (InterruptedException e) {
System.out.print(" t1-interrupted! ");
} finally {
System.out.println("t1 解锁");
lock.unlock();
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
//lock.lock(); //不能对interrupt()方法作出响应
lock.lockInterruptibly(); //也是上锁,但是可以对interrupt()方法作出响应
System.out.print(" t2 start... ");
TimeUnit.SECONDS.sleep(5);
System.out.print(" t2 end... ");
} catch (InterruptedException e) {
System.out.println(" t2-interrupted! ");
} finally {
try {
System.out.println("t2 解锁");
lock.unlock();
} catch (Exception e) {
System.out.println("没有得到锁的线程运行结束");
}
}
}, "t2");
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); //打断t2的等待,如果使用的是t2使用的是lock()方法进行上锁就无法打断,如果使用lockInterruptibl()方法就可以打断
}
}
```
运行结果
```
t1 start... t2-interrupted!
t2 解锁
没有得到锁的线程运行结束
```
## ReentrantLock可以指定为公平锁
```java
package demo25;
import java.util.concurrent.locks.ReentrantLock;
/*ReentrantLock可以指定为公平锁,构造方法中将fair属性设置为true即为公平锁,fair默认为false*/
public class T extends Thread {
private static ReentrantLock lock = new ReentrantLock(true); //参数为true表示为公平锁,可对比输出结果
@Override
public void run() {
for (int i = 0; i < 20; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "-获得锁 ");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
T t = new T();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
```
运行结果
```
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
Thread-1-获得锁
Thread-2-获得锁
```
## 面试题
写一个固定容量的同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。
### 使用wait和notifyAll方法来实现
下面使用wait和notifyAll方法来实现
```java
package demo26;
import java.util.LinkedList;
/**
* 写一个固定容量的同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。
* wait和notifyAll方法来实现。
*/
public class MyContainer1 {
final private LinkedList