Q:
实现一个容器,提供两个方法add、size,写两个线程:
线程1,添加10个元素到容器中
线程2,实时监控元素个数,当个数到达5个时,线程2给出提示并结束
初步想法:
程序1
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 16:21
* @Description:
*/
public class T01_withoutVolatile {
List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T01_withoutVolatile c = new T01_withoutVolatile();
new Thread(()->{
for (int i=0;i<10;i++){
c.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(c.size()==5){
break;
}
}
System.out.println("t2 结束");
},"t2").start();
}
}
运行结果:
实现与题目不符,原因:
- 方法没有加同步
- while(true)中的c.size()方法永远没有检测到,因为线程与线程之间是不可见的。
于是添加volatile解决上述问题
程序2
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 16:36
* @Description:
*/
public class T02_withVolatile {
volatile List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T02_withVolatile c = new T02_withVolatile();
new Thread(()->{
for (int i=0;i<10;i++){
c.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(c.size()==5){
break;
}
}
System.out.println("t2 结束");
},"t2").start();
}
}
使用volatile修饰List集合,实现线程间信息的传递。
运行结果:
仍然与题目不符,原因:
- volatile修饰引用类型,这个引用对象指向另外一个new出来的对象,如果这个对象的成员变量值改变了,是无法观察到的。
程序3
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 16:43
* @Description:
*/
public class T03_NotifyHoldingLock {
//添加volatile,使得t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(o);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T03_NotifyHoldingLock c = new T03_NotifyHoldingLock();
final Object lock = new Object();
//需要注意先启动t2再启动t1
new Thread(()->{
synchronized (lock){
System.out.println("t2 启动");
if(c.size()!=5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}
},"t2").start();
new Thread(()->{
System.out.println("t1 启动");
synchronized (lock){
for (int i=0;i<10;i++){
c.add(new Object());
System.out.println("add "+i);
if(c.size() == 5){
lock.notify();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t1").start();
}
}
利用synchronized锁的方式(利用wait()、notify()),通过给object对象加锁,然后调用wait()方法和notify()方法。
运行结果:
仍然不符合题意,因为:
- notify()方法不释放锁,当t1线程调用了notify()方法后,并没有释放当前的锁,所以t1还是会执行下去,等到t1执行完毕,t2线程才能被唤醒。
程序4:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 16:57
* @Description:
*/
public class T04_NotifyHoldingLock {
//添加volatile,使得t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(o);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T04_NotifyHoldingLock c = new T04_NotifyHoldingLock();
final Object lock = new Object();
//需要注意先启动t2再启动t1
new Thread(()->{
synchronized (lock){
System.out.println("t2 启动");
if(c.size()!=5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}
//通知t1继续执行
lock.notify();
},"t2").start();
new Thread(()->{
System.out.println("t1 启动");
synchronized (lock){
for (int i=0;i<10;i++){
c.add(new Object());
System.out.println("add "+i);
if(c.size() == 5){
lock.notify();
//释放锁,让t2得以执行
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t1").start();
}
}
程序4在程序3的基础上进行了一些小改动,调用wait()方法阻塞t1线程,释放锁,让t2得以执行,实现t2的实时监控。
运行结果:
list集合中的对象为5个时结束,符合题意。
我们考虑使用其他同步技术来实现该问题:
程序5:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 17:06
* @Description:
*/
public class T05_CountDownLatch {
//添加volatile,使t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T05_CountDownLatch c = new T05_CountDownLatch();
//需要注意先启动t2再启动t1
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
System.out.println("t2 启动");
if(c.size()!=5){
try {
latch.await();
} 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++) {
c.add(new Object());
System.out.println("add "+i);
if(c.size()==5){
//暂停t1线程
latch.countDown();
}
}
},"t1").start();
}
}
使用CountDownLatch(门闩)来实现,与程序4不同,没有了锁,采用await()方法替换t2线程和t1线程的wait()方法。
运行结果:
与题意不符,t2没有来得及去实时监控,对其进行改进。
程序6:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 17:17
* @Description:
*/
public class T06_CountDownLatch {
//添加volatile,使t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
T06_CountDownLatch c = new T06_CountDownLatch();
//需要注意先启动t2再启动t1
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
System.out.println("t2 启动");
if(c.size()!=5){
try {
latch.await();
} 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++) {
c.add(new Object());
System.out.println("add "+i);
if(c.size()==5){
//暂停t1线程
latch.countDown();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1").start();
}
}
程序6在程序5的基础上,实现在t1线程打开t2线程门闩的时候,给t1自己加一个门闩。
程序7:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 17:22
* @Description:
*/
public class T07_LockSupport {
//添加volatile,使t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
static Thread t1=null,t2=null;
public static void main(String[] args) {
T07_LockSupport c = new T07_LockSupport();
CountDownLatch latch = new CountDownLatch(1);
t2=new Thread(()->{
System.out.println("t2 启动");
if(c.size()!=5){
LockSupport.park();
}
System.out.println("t2 结束");
LockSupport.unpark(t1);
},"t2");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1=new Thread(()->{
System.out.println("t1 启动");
for (int i = 0; i < 10; i++) {
c.add(new Object());
System.out.println("add "+i);
if(c.size() ==5){
LockSupport.unpark(t2);
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1");
//需要注意先启动t2再启动t1
t2.start();
t1.start();
}
}
实现原理与程序5相同,只是利用LockSupport的park()方法和unpark()方法来实现。
程序8:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* @BelongsProject: test22
* @BelongsPackage: PACKAGE_NAME
* @Author: 高传威
* @CreateTime: 2020-07-19 17:36
* @Description:
*/
public class T08_Semaphore {
//添加volatile,使t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(0);
}
public int size(){
return list.size();
}
static Thread t1=null,t2=null;
public static void main(String[] args) {
T08_Semaphore c = new T08_Semaphore();
Semaphore s = new Semaphore(1);
t1=new Thread(()->{
try {
s.acquire();
for (int i = 0; i < 5; i++) {
c.add(new Object());
System.out.println("add "+i);
}
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t2.start();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
s.acquire();
for (int i = 5; i < 10; i++) {
c.add(new Object());
System.out.println("add "+i);
}
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
t2=new Thread(()->{
try {
s.acquire();
System.out.println("t2 结束");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2");
t1.start();
}
}
围绕这一问题,共采用5种技术来实现:
- volatile
- wait()和notify()
- CountDownLatch
- LockSupport
- Semaphore