这张我们讲讲多线程之间通信和ThreadGroup!
单线程间通信:
如果服务器端有若干个线程会从队列中获取想要的数据,进行异步处理,那么这些线程是如何知道队列中有数据呢?
1.一个比较笨的方法-不断轮询,如果队列中有数据,那么读取数据并且处理,如果没有 再次等待若干时间,再次轮询,
2.第二种方法为:通知机制如果队列中有数据,则通知现场开始工作,没有,则通知线程休息。
初始wait 和Notify
举个栗子
线程之间进行通信,首先实现一个EventQueue,该Queue有三种状态,
1.队列满了,最多可容乃多少个Event
2.队列空,当所有的Event都被处理,没有在提交Event的时候.
3.有Event但是没有满,有新的Event被提交,但此时没有上线。
public class EventQueue {
private final int max ;
static class Event{}
private final LinkedList<Event> eventQueue = new LinkedList<>();
private final static int DEFAULT_MAX_EVENT = 200;
public EventQueue(){
this(DEFAULT_MAX_EVENT);
}
public EventQueue(int max){
this.max = max;
}
private void console(String msg){
System.out.printf("%s :%s \n",Thread.currentThread().getName(),msg);
}
public void put(Event event){
synchronized(eventQueue){
while(eventQueue.size() >= max){
console("队里满了!");
try {
eventQueue.wait();
System.out.println("我在wait 方法后面");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
console("事件被提交!");
eventQueue.addLast(event);
eventQueue.notifyAll();
}
}
public Event take(){
synchronized(eventQueue){
while (eventQueue.isEmpty()){
try {
console("当前队列为空!");
eventQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Event event = eventQueue.removeFirst();
this.eventQueue.notifyAll();
console("这个事件被处理:"+event);
return event;
}
}
}
put(Event event);将当前event添加到LinkList的链表中,take();从LinkList拿到头部的Event。
wait和notify方法详解
wait()方法有三个重载方法。
public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout,int nanos)throws InterruptedException
wait方法的三个重载方法都调用wait(long timeout);这个方法,Object的wait(long timeout)方法会导致当前线程进入阻塞,直到有其他线程调用了Object的notify或者notifyAll方法,或者wait到了一定的时间而自动唤醒。
wait方法必须拥有该对象的Monitor,也就是wait方法必须在同步方法中使用。
当前线程执行了该对象的wait方法之后,将会放弃该montor的所有权并且进入与对象关联的wait set中,
notify 唤醒正在执行该对象wait方法,被唤醒的对象需要重新获取对该对象所关联monitor的lock才能继续执行。
关于wait和notify的注意事项
1.wait是可中断的方法,其他线程是可以调用interrupt是可以将其打断的。
2.如果线程执行了wait方法 ,会加入与之对应的wait set
3.当线程进入wait set之后,notify方法可以进行唤醒,也就是从wait set中弹出来。
4.必须在同步代码块中使用wait 和notify
5.同步代码的monitor必须与执行wait 和notify的方法的对象一致。也就是说那个对象进行同步,就只能用哪个对象进行wait和notify操作。
wait和sleep的区别
1.wait和sleep 都会使线程进入阻塞状态。都是可以中断的,中断后收到中断异常
2.wait是Object的方法,sleep是Thread特有的方法
3.wait必须要在同步代码块中使用,而sleep不需要
4.同步代码块中,执行sleep方法时,并不会释放monitor的锁,而wait方法会释放。
5.sleep方法如果指定休眠时间,休眠时间到了之后会主动退出阻塞,而wait方法(没有指定修改时间)则需要被其他线程中断后才能退出阻塞。
wait set介绍
在虚拟机规范中存在一个线程休息室(wait set) 的概念,wait set是什么样的数据结构 官方并没有给出规范,但是不管怎么样,当一个线程执行了wait方法后,都会进入到与改对象monitor关联的wait set中,并且释放monitor的所有权,一个线程调用该monitor的notify方法之后,其中一个线程会弹出来,至于是先进先出,还是随机 JVM并没有 给出规范。而执行notifyAll 则不需要考虑那个线程会被弹出,因为wait set中的所有wait线程都将被弹出。
notify方法只会唤醒一个线程 notifyAll方法会唤醒所有的线程
synchronized关键字的缺陷
1.synchronized提供了一种排它机制,某个线程获取monitor可能会被阻塞,而这种阻塞有两个很明显的缺陷,第一,无法控制阻塞时长,第二,阻塞不可被中断。
ThreadGroup 与Thread 介绍
在JAVA程序中,默认情况下,新的线程都会被加入到Main线程所在的group中,main线程的group名字同线程名,如同父子关系一样,ThreadGroup同样也存在父子关系,当创建一个线程的时候,当前线程都会被加入到一个ThreadGroup中,
Thread和ThreadGroup关系图
创建ThreadGroup
public ThreadGroup(String name)
public ThradGroup(ThreadGroup parent,String name)
方法一:指定了Thread的名字,但是该ThreadGroup的父ThreadGroup是创建它的线程所在的ThreadGroup;
方法二:ThreadGroup的构造函数赋予group名字的同时显示地指定了父group。
ThreadGroup的基本操作
-activeCount() 用于获取group中活跃的线程。这是个预估值,并不能保证100%准确,
-activeGroupCount() 用于获取group中活跃的字group,这也是个预估值。
-getName用于获取group的名字
-getParent()用于获取group的父group,如果父group不存在,则会返回null。比如System的group就为Null
-list() 该方法没有返回值,执行该方法会将group中所有的活跃线程信息全部输出到控制台,
-setMaxPriority(int priority)指定group的最大优先级,最大优先级不能超过父group的最大优先级,执行方法不仅会改变当前group的最大优先级,还会改变子group的最大优先级
-interrupt()一个ThreadGroup调用interrupt会导致group中所有active线程都被interrupt,也就是该group中的每一个线程的interrupt标识都被设置了,
-destory() 用于销毁ThreadGroup,该方法只是针对一个没有任何active线程的group进行destory标记,调用改方法的直接结果是在父group中将自己移除。
-daemon(),ThreadGroup也可以调用setDaemon方法,将当前ThreadGroup设置为守护ThreadGroup,如果一个ThradGroup设置为守护ThradGroup,如果该group中没有任何一个active线程的时候,该group将自动destory。
网友评论