实现一个容器,提供两个方法,add,size
写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
1、使用同步容器
注意:确保t2线程先运行,否者t1线程先运行size超过5时t2线程一直无法结束,导致程序无法退出
public class TestSynchronizedContainer {
volatile List lists = Collections.synchronizedList(new LinkedList<>());
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) throws InterruptedException {
T02_WithVolatile c = new T02_WithVolatile();
new Thread(() -> {
while(true) {
if(c.size() == 5) {
break;
}
}
//保证线程2先执行
System.out.println("t2 结束");
}, "t2").start();
Thread.sleep(1l);
new Thread(() -> {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i);
}
}, "t1").start();
}
}
2、使用wait和notify做到,wait会释放锁,而notify不会释放锁
需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以,在添加元素到5个的时候通知第二个线程执行,但是notif不会释放锁,所以如果t1不调用wait方法,则t1会继续执行,执行完后t2才能得到执行,也就是说t1的确是唤醒了t2,但是没有释放锁让t2执行。
public class WaitAndNotify{
//添加volatile,使t2能够得到通知
volatile List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
WaitAndNotify c = new WaitAndNotify();
final Object lock = new Object();
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();
try {
Thread.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
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();
}
}
}
}
}, "t1").start();
}
}
3、使用CountDownLatch,这里使用了两个CountDownLatch,分别控制两个线程,如果只有一个,即线程1添加元素到5个的时候通知线程2结束,这个时候线程1通知完后还会继续执行,可能线程1添加到8个的时候,线程2才真正停止。所以通知完线程2停止后线程1得阻塞住,线程2执行完后,再让线程1执行。
public class TestCountDownLatch {
// 添加volatile,使t2能够得到通知
volatile List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
TestCountDownLatch c = new TestCountDownLatch();
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latchT1 = new CountDownLatch(1);
new Thread(() -> {
System.out.println("t2启动");
if (c.size() != 5) {
try {
latch.await();
//也可以指定等待时间
//latch.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
latchT1.countDown();
}, "t2").start();
try {
Thread.sleep(1);
} catch (InterruptedException e1) {
e1.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) {
// 打开门闩,让t2得以执行
latch.countDown();
try {
latchT1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start();
}
}
4、使用LockSupport,这里和上面使用CountDownLatch类似,线程1通知线程2后,线程1继续执行,导致线程2没有及时停止,所以在通知线程2停止时也要让线程1阻塞住
public class TestLockSupport {
// 添加volatile,使t2能够得到通知
volatile List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
static Thread t1 = null, t2 = null;
public static void main(String[] args) {
TestLockSupport c = new TestLockSupport();
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();
}
}
}, "t1");
t2 = new Thread(() -> {
System.out.println("t2启动");
LockSupport.park();
System.out.println("t2 结束");
LockSupport.unpark(t1);
}, "t2");
t2.start();
t1.start();
}
}
5、使用Semaphore,并使t1线程控制t2线程的启动。
public class TestSemaphore {
// 添加volatile,使t2能够得到通知
volatile List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
static Thread t1 = null, t2 = null;
public static void main(String[] args) {
TestSemaphore c = new TestSemaphore();
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++) {
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();
}
}