实现一个容器,提供两个方法,add,size
写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
1.第一种方法是使用while一直监听容器的数量变化,个数到达5就退出
缺点: list添加volatile之后,第二个线程能够接到通知,但是,线程的死循环很浪费cpu
public class Test1 {
private 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) {
final Test1 t = new Test1();
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
t.add(i);
System.out.println("线程2增加了" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
System.out.println("线程2开始");
while(true) {
if(t.size() == 5) {
System.out.println("线程2退出");
break;
}
}
}
}.start();
}
}
2.第二种方法是使用等待通知机制,这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
第一个线程判断个数不到5就wait 该线程停止执行并释放锁,第二个线程得以执行 把容器个数增加到5就调用notify唤醒线程1,这时候第二个线程要wait释放锁,让第一个线程执行,第一个线程执行结束前,要调用notify唤醒第二个线程,让第二个线程得以继续执行
public class Test2 {
private 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) {
final Test2 t = new Test2();
final Object object = new Object();
new Thread() {
public void run() {
System.out.println("线程2开始");
if(t.size() != 5) {
synchronized (object) {
try {
object.wait();//个数不到5 调用wait()方法,释放锁
object.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("线程2结束");
}
}.start();
new Thread() {
public void run() {
System.out.println("线程1开始");
synchronized (object) {
for (int i = 0; i < 10; i++) {
t.add(i);
System.out.println("线程1添加" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if(t.size() == 5) {
object.notify();
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
System.out.println("线程1结束");
}
}.start();
}
}
3.使用Latch(门闩)替代wait notify来进行通知
好处是通信方式简单,同时也可以指定等待时间
使用await和countdown方法替代wait和notify
CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了
这时应该考虑countdownlatch/cyclicbarrier/semaphore
public class Test3 {
private 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) {
final Test3 t = new Test3();
final CountDownLatch latch = new CountDownLatch(1);//这里是闩上一个门
new Thread() {
public void run() {
System.out.println("线程1启动");
if(t.size() != 5) {
try {
latch.await();//调用该方法,下面的代码停止执行,被闩住了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程1结束");
}
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
t.add(i);
System.out.println("线程2添加了" + i);
if(t.size() == 4) {
// 打开门闩,让另一个线程得以执行
latch.countDown();
}
}
}
}.start();
}
}
运行的结果
线程1启动
线程2添加了0
线程2添加了1
线程2添加了2
线程2添加了3
线程2添加了4
线程1结束
线程2添加了5
线程2添加了6
线程2添加了7
线程2添加了8
线程2添加了9