线程间的通信(二)

多线程间通信
notify每次只唤醒一个线程,jvm的wait set 唤醒规则是先进先出的方式弹出。
notifyAll()唤醒wait set里全部的阻塞线程,被唤醒的线程同样需要继续争抢motior锁。
wait set 线程休息室,线程调用某个对象的wait方法后就会进入到与对象关联的wait set中,并且释放monitor所有权。
注意: 对于临界值的判断使用while而不是if,因为while时循环判断,在线程被唤醒并获取到monitor所有权时会再判断一次,保证临界值的一致性,而if只会判断一次,可能会导致临界值的判断失效。

public class DubleCommun {
	private final int max;
	private final LinkedList<Event> eventQueue = new LinkedList<Event>();
	private final static int DEFAULT_MAX_EVENT = 10;
	static class Event{
		
	}
	public DubleCommun() {
		this(DEFAULT_MAX_EVENT);
	}
	public DubleCommun(int max) {
		this.max = max;
	}
	
	public void off(Event event) {
		synchronized (eventQueue) {
			while(eventQueue.size()>=max) {
				console("the queue is full");
				try {
					eventQueue.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			console("the new event is submitted"+eventQueue.size());
			eventQueue.addLast(event);
			eventQueue.notifyAll();
		}
	}
	
	public Event task() {
		synchronized (eventQueue) {
			while(eventQueue.isEmpty()) {
				console("the queue is empty");
				try {
					eventQueue.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			Event event = eventQueue.removeFirst();
			this.eventQueue.notifyAll();
			console("the event "+ event+"is handled"+eventQueue.size());
			return event;
		}
	}
	
	private void console(String msg) {
		System.out.println(Thread.currentThread().getName()+"  "+msg);
	}
}

//测试代码

	public static void main(String[] args) {
		final DubleCommun eventQueue = new DubleCommun();
		for(int i = 1;i<5;i++) {
			new Thread(()->{
				for(;;) {
					eventQueue.off(new DubleCommun.Event());
				}
				},"生产者线程"+i).start() ;
		}
		 
		for(int i = 1;i<5;i++) {
			new Thread(()->{
				for(;;) {
					eventQueue.task();
					try {
						TimeUnit.MILLISECONDS.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				},"消费者线程"+i).start() ;
		}
		
	}

部分输出
生产者线程2 the queue is full
生产者线程3 the queue is full
生产者线程4 the queue is full
生产者线程1 the queue is full
消费者线程4 the event communication.DubleCommunEvent@50d719b0ishandled9消费者线程1theeventcommunication.DubleCommunEvent@50d719b0is handled9 消费者线程1 the event communication.DubleCommunEvent@50d719b0ishandled9线1theeventcommunication.DubleCommunEvent@c54e927is handled8
消费者线程3 the event communication.DubleCommunEvent@4de9d576ishandled7消费者线程2theeventcommunication.DubleCommunEvent@4de9d576is handled7 消费者线程2 the event communication.DubleCommunEvent@4de9d576ishandled7线2theeventcommunication.DubleCommunEvent@f009885is handled6
生产者线程1 the new event is submitted6
生产者线程1 the new event is submitted7
生产者线程1 the new event is submitted8
生产者线程1 the new event is submitted9
生产者线程1 the queue is full
生产者线程4 the queue is full
生产者线程3 the queue is full
生产者线程2 the queue is full

自定义显示锁 BooleanLock
synchronized关键字的缺陷,无法控制阻塞时长,阻塞不可被中断。构造一个 BooleanLock,让其具备synchronized的功能,又具备可中断和lock超时的功能。

定义lock接口

import java.util.List;
import java.util.concurrent.TimeoutException;

public interface MyLock {
	/**
	 * 
	 * @Title lock
	 * @throws InterruptedException
	 * @Description 方法永远阻塞,但是该方法时可中断的
	 * @throws
	 */
	void lock() throws InterruptedException;
	/**
	 * 
	 * @Title lock
	 * @param mills
	 * @throws InterruptedException
	 * @throws TimeoutException
	 * @Description 方法在规定时间内阻塞 方法可中断
	 * @throws
	 */
	void lock(long mills) throws InterruptedException,TimeoutException,IllegalArgumentException;
	/**
	 * 
	 * @Title unlock
	 * @Description 释放锁
	 * @throws
	 */
	void unlock();
	/**
	 * 
	 * @Title getBlockedThreads
	 * @return
	 * @Description  获取当前阻塞线程
	 * @throws
	 */
	List<Thread> getBlockedThreads();
}

实现lock接口

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

public class MyBooleanLock implements MyLock {
	//当前拥有锁的线程
	private Thread currentThread;
	//是否被获取锁
	private boolean locked = false;
	//储存那些获取锁进入阻塞的线程
	private final List<Thread> blocked = new ArrayList<Thread>();
	
	@Override
	public void lock() throws InterruptedException {
		synchronized (this) {
			while(locked) {
				blocked.add(currentThread);
				this.wait();
			}
			blocked.remove(currentThread);
			this.locked = true;
			this.currentThread = Thread.currentThread();
		}
	}

	@Override
	public void lock(long mills) throws InterruptedException, TimeoutException,IllegalArgumentException {
		synchronized (this) {
			if(mills<=0) {
				 throw new IllegalArgumentException("mills 必须大于0");
			}else {
				long remain = mills;
				long endmain = System.currentTimeMillis()+remain;
				while(locked) {
					if(remain<=0) {
						throw new TimeoutException("时间超时");
					}
					if(!blocked.contains(currentThread)) {
						blocked.add(currentThread);
					}
					this.wait(remain);
					remain = endmain - System.currentTimeMillis();
				}
				blocked.remove(currentThread);
				this.locked=true;
				this.currentThread=Thread.currentThread();
			}
			
		}
	}

	@Override
	public void unlock() {
		synchronized (this) {
			if(currentThread.equals(Thread.currentThread())) {
				this.locked=false;
				System.out.println(Thread.currentThread().getName()+" "+"放弃monitor所有权");
				this.notifyAll();
			}
		}
		
	}

	@Override
	public List<Thread> getBlockedThreads() {
		return this.blocked;
	}

}

测试多线程争抢lock

	private final MyLock mylock = new MyBooleanLock();
	public void syncMethod() {
			try {
				mylock.lock();
				System.out.println(Thread.currentThread().getName()+" "+"获得锁");
				TimeUnit.MILLISECONDS.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				mylock.unlock();
			}
	}
	public static void main(String[] args) {
		MyBooleanLockTest bl =new MyBooleanLockTest();
		IntStream.range(0, 5)
				.mapToObj(i-> new Thread(bl::syncMethod))
				.forEach(Thread::start);
								

	}

输出
Thread-0 获得锁
Thread-0 放弃monitor所有权
Thread-4 获得锁
Thread-4 放弃monitor所有权
Thread-1 获得锁
Thread-1 放弃monitor所有权
Thread-3 获得锁
Thread-3 放弃monitor所有权
Thread-2 获得锁
Thread-2 放弃monitor所有权
从输出可以看到每次只有一个线程获取到了lock;
线程中断测试

	private final MyLock mylock = new MyBooleanLock();
	public void syncMethod() {
			try {
				mylock.lock();
				System.out.println(Thread.currentThread().getName()+" "+"获得锁");
				TimeUnit.MILLISECONDS.sleep(10);
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName()+" 线程被中断");
			}finally {
				mylock.unlock();
			}
	}
	public static void main(String[] args) throws InterruptedException    {
		MyBooleanLockTest bl =new MyBooleanLockTest();
		new Thread(bl::syncMethod,"线程1").start();
		TimeUnit.MILLISECONDS.sleep(2);
		Thread t2 = new Thread(bl::syncMethod,"线程2");
		t2.start();
		TimeUnit.MILLISECONDS.sleep(10);
		t2.interrupt();
								
	}

输出
线程1 获得锁
线程1 放弃monitor所有权
线程2 获得锁
线程2 线程被中断
线程2 放弃monitor所有权
可以看出线程2中断的异常InterruptedException 被捕获。

阻塞线程超时测试

	public static void main(String[] args) throws InterruptedException    {
		MyBooleanLockTest bl =new MyBooleanLockTest();
		new Thread(bl::syncMethod1,"线程1").start();
		TimeUnit.MILLISECONDS.sleep(2);
		Thread t2 = new Thread(bl::syncMethod1,"线程2");
		t2.start();
		TimeUnit.MILLISECONDS.sleep(10);
	}
	public void syncMethod1() {
		try {
			mylock.lock(1000);
			System.out.println(Thread.currentThread().getName()+" "+"获得锁");
			TimeUnit.SECONDS.sleep(10);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (InterruptedException e) {
			
		}finally {
			mylock.unlock();
		}
	}

输出
线程1 获得锁
java.util.concurrent.TimeoutException: 线程线程2时间超时
at communication.MyBooleanLock.lock(MyBooleanLock.java:39)
at communication.MyBooleanLockTest.syncMethod1(MyBooleanLockTest.java:32)
at java.lang.Thread.run(Unknown Source)
线程1 放弃monitor所有权

线程获取锁等待时间为1000毫秒,而线程1休眠时间是10秒,线程2最后是在1000毫秒内没有获取到monitor所有权,抛出了超时异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值