美文网首页
JAVA多线程(五)

JAVA多线程(五)

作者: 以南之南_b9a1 | 来源:发表于2019-05-21 19:45 被阅读0次

    这张我们讲讲多线程之间通信和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。

    相关文章

      网友评论

          本文标题:JAVA多线程(五)

          本文链接:https://www.haomeiwen.com/subject/aezjaqtx.html