使用wait/notify模拟Queue+ThreadLocal

本文介绍了一个自定义的阻塞队列实现,通过线程间的同步与等待确保元素的正确添加与移除。此外,还探讨了ThreadLocal类的应用,通过为每个线程提供独立的变量副本来避免线程安全问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在main函数中设置队列的容量为5,并添加了a、b、c、d、e这5个元素。在子线程t1中,想要将元素f、g添加进入队列,但是由于队列已满,执行lock.wait(),释放lock对象锁。t2线程从队列中取出两个元素,它获取到lock对象锁之后,因此时队列容量已满为5个,则从队列中取出元素并执行lock.notify发送通知给t1,t1等待t2中取出一个元素的代码执行完毕并释放lock锁之后,t1获得lock锁就可以向队列中添加一个元素了。


import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class MyQueue {
	
	//1 需要一个承装元素的集合 
	private LinkedList<Object> list = new LinkedList<Object>();
	
	//2 需要一个计数器
	//AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。
	private AtomicInteger count = new AtomicInteger(0);
	
	//3 需要制定上限和下限
	private final int minSize = 0;//声明时初始化
	
	private final int maxSize ;
	
	//4 构造方法
	public MyQueue(int size){
		this.maxSize = size;//在构造方法中初始化
	}
	
	//5 初始化一个对象 用于加锁
	private final Object lock = new Object();
	
	
	//put(anObject): 把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间再继续.
	public void put(Object obj){
		synchronized (lock) {
			while(count.get() == this.maxSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {//中断异常
					e.printStackTrace();
				}
			}
			//1 加入元素
			list.add(obj);
			//2 计数器累加
			count.incrementAndGet();
			//3 通知另外一个线程(唤醒)
			lock.notify();
			System.out.println("新加入的元素为:" + obj);
		}
	}
	
	
	//take: 取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入.
	public Object take(){
		Object ret = null;
		synchronized (lock) {
			while(count.get() == this.minSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//1 做移除元素操作
			ret = list.removeFirst();
			//2 计数器递减
			count.decrementAndGet();
			//3 唤醒另外一个线程
			lock.notify();
		}
		return ret;
	}
	
	public int getSize(){
		return this.count.get();
	}
	
	
	public static void main(String[] args) {
		
		final MyQueue mq = new MyQueue(5);
		mq.put("a");
		mq.put("b");
		mq.put("c");
		mq.put("d");
		mq.put("e");
		
		System.out.println("当前容器的长度:" + mq.getSize());
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				mq.put("f");
				mq.put("g");
			}
		},"t1");
		
		t1.start();
		
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				Object o1 = mq.take();
				System.out.println("移除的元素为:" + o1);
				Object o2 = mq.take();
				System.out.println("移除的元素为:" + o2);
			}
		},"t2");
		
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		t2.start();
	}
}

Eclipse中console输出


t2取走队列中一个元素后,释放锁并执行System.out.println("移除的元素为:" + o1);。与此同时,t1获得对象锁并执行put方法中lock.wait后面的代码,并System.out.println("新加入的元素为:" + obj);。上图中椭圆标注的部分,由于t1与t2同时运行,执行各自代码的时间点的先后顺序导致。


ThreadLocal

ThreadLocal概念:线程局部变量,是一种多线程间并发访问变量的解决方案。它完全不提供锁,而是使用空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。从性能上来说,ThreadLocal不具有绝对的优势,在并发不是很高的情况下,加锁的性能会更好,但是作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争。

public class ConnThreadLocal {

	public static ThreadLocal<String> th = new ThreadLocal<String>();
	
	public void setTh(String value){
		th.set(value);
	}
	public void getTh(){
		System.out.println(Thread.currentThread().getName() + ":" + this.th.get());
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		final ConnThreadLocal ct = new ConnThreadLocal();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				ct.setTh("张三");
				ct.getTh();
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
					ct.getTh();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t2");
		
		t1.start();
		t2.start();
	}
	
}

在t1、t2两个线程中,都存在变量的本地副本。所以,在t1中修改对象的th值,在t2中是获取不到的。



接下来,我将从代码实现的角度来为大家详细的解析Handler-Looper-MessageQueue模型 Message.java package MyMessage; import MyHandler.Handler; public class Message { public int what; public Object obj; public Handler target; public long when; public Message next; } MessageQueue.java package MyMessageQueue; import MyMessage.Message; public class MessageQueue { private Message head; // 入队方法; public synchronized void enqueueMessage(Message msg, long delayMillis) { msg.when = System.currentTimeMillis() + delayMillis; // 空队列 或 新的最早消息; if (head == null || msg.when < head.when) { msg.next = head; head = msg; notify(); return; } Message p = head; while (p.next != null && p.next.when <= msg.when) { p = p.next; } msg.next = p.next; p.next = msg; } // 出队方法; public synchronized Message next() { for (;;) { long now = System.currentTimeMillis(); if (head == null) { waitUninterruptibly(0); continue; } long diff = head.when - now; if (diff <= 0) { Message m = head; head = m.next; m.next = null; return m; } waitUninterruptibly(diff); } } private void waitUninterruptibly(long millis) { boolean interrupted = false; try { while (true) { try { if (millis == 0) { wait(); } else { wait(millis); } return; } catch (InterruptedException e) { interrupted = true; } } } finally { if (interrupted) Thread.currentThread().interrupt(); } } } Hander.java package MyHandler; import MyLooper.Looper; import MyMessage.Message; import MyMessageQueue.MessageQueue; public class Handler { private final Looper mLooper; private final MessageQueue mQueue; public Handler() { this(Looper.myLooper()); } public Handler(Looper looper) { if (looper == null) throw new RuntimeException("Can't create Handler inside thread without Looper"); mLooper = looper; mQueue = looper.mQueue; } public void sendMessage(Message msg) { msg.target = this; mQueue.enqueueMessage(msg, 0); } public void sendMessageDelayed(Message msg, long delayMillis) { msg.target = this; mQueue.enqueueMessage(msg, delayMillis); } public void post(Runnable r) { Message m = new Message(); m.obj = r; sendMessage(m); } public void dispatchMessage(Message msg) { if (msg.obj instanceof Runnable) { ((Runnable) msg.obj).run(); } else { handleMessage(msg); } } public void handleMessage(Message msg) { } } Looper.java package MyLooper; import MyMessage.Message; import MyMessageQueue.MessageQueue; public class Looper { private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>(); public final MessageQueue mQueue; private Looper() { mQueue = new MessageQueue(); } public static void prepare() { if (sThreadLocal.get() != null) { throw new IllegalStateException("Only one Looper per thread"); } sThreadLocal.set(new Looper()); } public static Looper myLooper() { return sThreadLocal.get(); } public static void loop() { Looper me = myLooper(); if (me == null) throw new IllegalStateException("No Looper; call prepare() first"); for (;;) { try { Message msg = me.mQueue.next(); if (msg == null) continue; msg.target.dispatchMessage(msg); } catch (Exception e) { Thread.currentThread().interrupt(); break; } } } } Demo.java import MyLooper.Looper; import MyMessage.Message; import MyHandler.Handler; public class Demo { public static void main(String[] args) { // 创建并启动工作线程 new WorkerThread("WorkerThread").start(); // 给主线程一点时间确保工作线程初始化完成 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 主线程发送消息 System.out.println("MainThread: 准备发送消息"); WorkerThread.handler.sendMessage(WorkerThread.obtainMessage(1, "普通消息")); WorkerThread.handler.sendMessageDelayed(WorkerThread.obtainMessage(2, "延迟消息"), 3000); WorkerThread.handler.post(() -> System.out.println("WorkerThread: 执行Runnable任务 - " + Thread.currentThread().getName()) ); System.out.println("MainThread: 所有消息已发送"); } } class WorkerThread extends Thread { public static Handler handler; // 静态便于主线程访问 public WorkerThread(String name) { super(name); } @Override public void run() { // 初始化Looper和Handler Looper.prepare(); handler = new Handler() { @Override public void handleMessage(Message msg) { // 处理消息 System.out.println(getName() + ": 收到消息 - what=" + msg.what + ", obj=" + msg.obj); } }; System.out.println(getName() + ": Looper已准备, 等待消息..."); Looper.loop(); // 开始消息循环 } // 辅助方法创建消息 public static Message obtainMessage(int what, Object obj) { Message msg = new Message(); msg.what = what; msg.obj = obj; return msg; } }
最新发布
08-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值