[exercise-001] 启动两个线程完成同步读写,练习使用synchronized块/wait()/notify()

本文目的

现在有一个小需求,需要启两个线程A和B,线程B负责让队列里加元素(加100个),线程A实时追踪线程B的操作,读出队列的元素个数。最后线程A(读到队列中有100个后)先结束,然后线程B结束。

code

package learningThreads.exercises.taobao1;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/* 启动两个线程,线程A负责往队列里添加数据,线程B负责实时报出队列中的元素个数 */
public class ThreadVisibilityTest {

	List list = new ArrayList();

	public void add(Object o) {
		list.add(o);
	}

	public int size() {
		return list.size();
	}

	public static void main(String[] args) {
		ThreadVisibilityTest tvt = new ThreadVisibilityTest();
		final Object lock = new Object();
		new Thread(() -> {
			synchronized (lock) {
				while (true) {
					System.out.println("from A: " + tvt.size());
					if (tvt.size() == 100) {
						lock.notify();
						System.out.println("线程A结束");
						break;
					}
					try {
						lock.notify();
						lock.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
//					try {
//						System.out.println("睡了1s...");
//						TimeUnit.SECONDS.sleep(1);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
				}

			}
		}, "线程A").start();

		try {
//			System.out.println("睡了2s...");
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			System.out.println("睡了2s...");
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		new Thread(() -> {
			synchronized (lock) {
				while (true) {
					if (tvt.size() < 100) {
						tvt.add(new Object());
						System.out.println("from B: add one");
						lock.notify();
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					} else {
						break;
					}

				}

			}
			System.out.println("线程B结束");
		}, "线程B").start();

	}

}

输出

from A: 0  //如果有sleep(1)那块代码,这句话大概率先出来
睡了2s...  //如果没有加sleep(1)那块代码,这句话大概率先出来
from B: add one
from A: 1
from B: add one
from A: 2
from B: add one
from A: 3
...
...
from B: add one
from A: 99
from B: add one
from A: 100
线程A结束
线程B结束

简单分析

  1. 首先,我们在public类ThreadVisibilityTest先内置了一个队列,包装了ArrayList的add和size方法。这个list会被接下来的两个线程使用到。
  2. main方法里,我们有一个锁lock,然后是线程A的定义,用synchronized块包住,如果检查到list的载荷达到100,则监控结束,这个时候第28行的lock.notify()很重要,它用来将线程B从等待池拿到lock的竞争池。如果没有打到100,那么也要通知线程B,然后自己wait(),就是释放锁,自己阻塞在那里。
  3. 线程B也是synchronized块里面一个while循环,它判断list的载荷没有到达100,就往里加,每加一个就notify一下线程A(因为线程A调用wait()了以后就会进入等待池,如果没有人notify它,它就会一直呆在里面,即使线程B的synchronized块执行完锁释放了,呆在等待池里的线程A都不会去拿这个锁,导致一直等待。。。。)。然后就是wait(),把线程B自己阻塞,让锁竞争池中的线程A拿到锁去报数。
  4. 所以这样一环扣一环下去,线程A会先知道list的载荷已经达到100,notify一下线程B,自己就打印"线程A结束",然后break出while循环,永远地结束自己的synchronized块。
  5. 线程B这时候被notify了,就从第71行继续往下,到新一轮while循环,判断size,就直接走到else块break出去了。

小结

  • synchronized可以保证块内的操作整体的原子性,而且访问的内存是线程间可见的,因此list在声明的时候不加volatile也可以。
  • 大家可以看到线程A start()后,接了两个sleep,前面一个睡了1秒,后面一个睡了2秒,如果把第一个睡眠的代码去掉,那么很可能是先打印"睡了2s…",后打印"from A: 0", 因为start之后线程不会马上执行,而是进入就绪队列,操作系统可能会先执行掉主线程里的睡眠2秒那个代码,所以故意加了一秒钟,让线程A尽可能先被执行,当然也可以不加,不影响目的的实现。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值