Java并发编程基础(三)

线程间通讯

线程间通讯称为进程内通讯,多个线程实现互斥访问共享资源时会互相发送信号货这等待信号,好比线程等待数据到来的通知,线程收到变量改变的信号。多线程

线程阻塞(同步)和非阻塞(异步)

1.线程阻塞 每一个请求都会建立一个线程去处理请求,这样的结果就致使了线程频繁的建立消耗很大,没法高吞吐的处理数据,大量的业务线程会频繁的CPU上下文切换,下降了系统的处理能力.异步

2.线程非阻塞 服务有若干的线程,不须要每次频繁的建立,在必定程度上减小了建立线程的消耗,线程不须要频繁的在CPU上频繁的切换(由于若干的线程数量是有限的),客户端也不须要等待处理好结果后返回结果,提升了系统的吞吐量。可是客户端想获得处理结果需再次请求接口(这个可使用接口回调的方式解决)。this

单线程间的通讯

wait和notify

  1. wait有三个重载的方法,其中wait(0)表明永不超时。线程

  2. wait(long timeout)方法会致使当前线程进入阻塞,直到线程调用了Object的notify或者nitifyAll方法才能将其唤醒,或者阻塞时间到达了timeout时间而自动唤醒。code

  3. wait方法必须用于该对象monitor,也就是wait方法必需要同步的方法使用。对象

  4. 当前线程执行了该对象的wait方法以后,将会放弃对该monitor的全部全且进入与该对象关联的wait set中,也就是说一旦线程执行了某个object的wait方法以后,他就会释放对该对象的monitor的全部权,其余线程也会有机会继续争夺该monitor的全部权。blog

例子:接口

public class EventQueueDemo {
	private final int max;
	static class Event {
	}
	private final LinkedList<Event> eventQueue = new LinkedList<>();
	private final static int DEFAULT_MAX_EVENT = 10;

	public EventQueueDemo() {
		this(DEFAULT_MAX_EVENT);
	}

	public EventQueueDemo(int max) {
		this.max = max;
	}
	public void offer(Event event) {
		synchronized (eventQueue) {
			if (eventQueue.size() >= max) {
				try {
					console("the queue is full");
					eventQueue.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			console("the new event is submitted");
			//若是没有满就将生产一个,将他放入队列的末尾。
			eventQueue.addLast(event);
			//告知消费者线程去消费
			eventQueue.notify();
		}
	}
	public Event take() {
		synchronized (eventQueue) {
			if (eventQueue.isEmpty()) {
				try {
					console("the queue is empty.");
					eventQueue.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//消费获取队头的实例
			Event event = eventQueue.removeFirst();
			//通知生产这线程去生产
			this.eventQueue.notify();
			console("the event " + event + " is handle.");
			return event;
		}
	}
	private void console(String message) {
		System.out.printf("%s:%s\n", currentThread().getName(), message);
	}
	public static void main(String[] args) {
		final EventQueueDemo eventQueue = new EventQueueDemo();
		//生产线程
		new Thread(() -> {
			while (true)
			eventQueue.offer(new EventQueueDemo.Event());
		}, "Producer").start();
		//消费线程
		new Thread(() -> {
			while (true) {
				eventQueue.take();
				try {
					TimeUnit.MILLISECONDS.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "Consumer").start();
	}
}

执行结果:队列

使用wait和notify的注意事项进程

  • wait方法是可中断的方法,这也就意味着,当前线程一旦调用了wait方法进入阻塞状态,其余线程是可使用interrupt方法将其打断的,可中断的方法被打断以后会收到中断一次InterruptedException,同时interrupt标识也会被擦出。

  • 线程执行了某个对象的wait方法之后,会加入与之对应的wait set中,没个对象的monitor都有一个与之关联的wait set

  • 当线程的进入了wait set以后,notify方法就能够将其唤醒,也就是从wait set中弹出,同时中断wait中的线程也会欸唤醒。

  • 必须在同步的方法重视那个使用wait方法和notify方法,由于执行了wait和notify的前提添加是必须持有同步的方法的monitor的全部权,不然将会收到IllegalMonitorStateException。

  • 同步代码的monitor必须与执行wait notify 方法的对象一致,简单的说就是用哪一个对象的monitor进行同步,就只能用哪一个对象进行wait和notify操做.

wait和sleep的区别

wait和sleep均可以使用当前的线程进入阻塞的状态,可是这两个方法仍是有本质上的区别的。

相同点:

1.wait和sleep均可以使得当前的线程进入阻塞状态.

2.wait和sleep方法军事可中孤单的方法,被中断后会收到中孤单异常。

不一样点:

1.wait事Object的方法,而sleep是Thread特有的方法.

2.wait方法的执行必须是在同步方法中执行的,sleep能够不须要。

3.线程执行sleep的时候,并不会释放monitor的锁,而执行wait的方法会释放monitor的锁。

4.sleep方法短暂休眠以后会主订的退出阻塞,而wait方法(没有指定的wait的时间)则须要被气压线程中断后才可以退出阻塞。

多线程间通讯

生产,消费

多线程间的通讯须要使用到Object的notifyAll方法,该方法与notify比较相似,均可以唤醒因为调用了wait方法而阻塞的线程,可是notify方法只能唤醒其中的一个线程,而notifyAll方法能够同时唤醒全国不的则色的线程,统一被唤醒的线程让然须要继续争夺monitor的锁。

须要注意的地方。 上面的例子只适合再单个生产着和单个消费者的场景使用,在多个生产着和多个消费者的时候会出校生产的大于最大值了还在生产,或者队列为空了还再消费。

须要改进就只须要讲判断的地方该成循环的。

改进的例子:

public class EventQueueMultiThreadDemo {

	private final int max;

	static class Event {

	}

	private final LinkedList<Event> eventQueue = new LinkedList<>();
	private final static int DEFAULT_MAX_EVENT = 10;

	public EventQueueMultiThreadDemo() {
		this(DEFAULT_MAX_EVENT);
	}

	public EventQueueMultiThreadDemo(int max) {
		this.max = max;
	}

	public void offer(Event event) {
		synchronized (eventQueue) {
			while (eventQueue.size() >= max) {
				try {
					console("the queue is full");
					eventQueue.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			console("the new event is submitted");
			eventQueue.addLast(event);
			eventQueue.notify();
		}
	}

	public Event take() {
		synchronized (eventQueue) {
			while (eventQueue.isEmpty()) {
				try {
					console("the queue is empty.");
					eventQueue.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			Event event = eventQueue.removeFirst();
			this.eventQueue.notify();
			console("the event " + event + " is handle.");
			return event;
		}
	}

	private void console(String message) {
		System.out.printf("%s:%s\n", currentThread().getName(), message);
	}

	public static void main(String[] args) {
		final EventQueueMultiThreadDemo eventQueue = new EventQueueMultiThreadDemo();
		new Thread(() -> {
			while (true)
			eventQueue.offer(new EventQueueMultiThreadDemo.Event());
		}, "Producer").start();

		new Thread(() -> {
			//这里该成循环判断,避免同一类的线程没有判断就
			//直接又往下跑了。
			while (true) {
				eventQueue.take();
				try {
					TimeUnit.MILLISECONDS.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}, "Consumer").start();

		new Thread(() -> {
			//这里该成循环判断,避免同一类的线程没有判断就
			//直接又往下跑了。
			while (true) {
				eventQueue.take();
				try {
					TimeUnit.MILLISECONDS.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}, "Consumer2").start();
	}
相关文章
相关标签/搜索