高级多线程控制类

  • Post author:
  • Post category:其他


在讲解高级多线程控制类之前,我们需要把前面的一点知识再补上:并且对比一下几种方法:

yield() 方法: 当前正在执行的线程(Running 状态),如果调用了yield方法,代表着它自愿的放弃了对CPU资源的占用,转化为runnable状态。

sleep(Time) : 线程休眠一段时间,但是休眠过程中仍然保持着对对象锁的占用

wait : 线程等待,会释放掉对对象锁的占用

join() : 一个线程使用了join方法,代表着必须等待着自己执行完毕,其他线程才能继续执行, 一般用于另一个线程启动之前调用, t 和 t1 是两个线程

      t.start(); // Line 15
      t.join(); // Line 16
      t1.start();

interrupt(中断): 中断是一个信号量(true, false),会对sleep,wait,join起作用,线程会不断检测这个信号量,通过抛出InterruptedException对中断做出回应, 用于通知处于这些状态的线程可以从这些状态中走出来了,从而恢复线程

synchronized 是一种重量级的同步方式,多个线程一旦要同时访问某一synchronized块,就会发生等待,然后线程就得一致等下去,现在Java提供了机制进行并发的控制,

在 java.util.concurrent 包下

补充的一个知识点: synchronized 修饰的成员变量应该是私有的,如果是共有的,就可以直接通过对象来调用进行修改了,synchronized 就不起作用了

接下来,我们简单介绍一下高级多线程控制类里的几个重要的类:


线程池(使用ThreadPoolExecutor创建线程池,而不是Executor创建线程池):


如果并发过程中线程过多的话,就会频繁的创建线程,销毁线程,大大的提高了系统的开销,耗费了大量的时间,这个时候我们就可以使用线程池技术,如果不使用的话,有可能造成系统创建大量的同类线程而消耗完内存,或者出现多线程过度切换的问题。

通过 Executors (是一个接口)提供四种线程池 :

CachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

FixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

SingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

但是注意的是,

在阿里的并发开发规范中,强制的要求了不允许使用Executors 来构建线程池,而是 使用 ThreadPoolExecutors去创建线程池


这是因为FixedThreadPool 和 SingleThreadExecutor 允许的请求队列的最大长度为 Integer.MAX_VALUE, 可能会堆积大量的请求,从而导致OOM(out of Memory 内存耗尽)。

CachedThreadPool 和 ScheduledThreadPool 允许的创建线程的最大长度为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致OOM


ThreadPoolExecutor类中提供了四种构造方法

:无外乎是下面几个参数的组合,参数的含义如下:默认创建的线程池是一个空的线程池,

而且该线程池还会动态的扩容,当任务已经放满了缓存队列的时候,就会触发扩容,创建新的线程,知道达到最大线程数

还有几个比较重要的方法:


execute()方

法: 可以向线程池提交一个任务,交由线程池去执行


submit()方法

: 这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。


shutdown()



shutdownNow()

是用来关闭线程池的。

public ThreadPoolExecutor(
  int corePoolSize, // 核心池的大小,(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
  int maximumPoolSize, // 线程池中的最大容忍线程数
  long keepAliveTime, // //线程存活的时间,指如果某个线程没有任务执行时多长时间会被终止,默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用   
  TimeUnit unit, // keepAliveTime 的时间单位
  BlockingQueue<Runnable> workQueue,  //任务缓存队列,用来存放等待执行的任务
  ThreadFactory threadFactory, //线程工厂,用来创建线程
  RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }


容器类: BlockingQueue 和 ConcurrentHashMap

BlockingQueue : 是一个接口,通常使用的是它的4种具体的实现类:是线程安全的类。阻塞队列,一般是向队尾添加新的元素,从队头取元素。

几个重要的方法:


put()

: 带阻塞的放元素,当队列中对象的个数大于所设置的最大值的时候,会阻塞


take()

: 带阻塞的取元素,当队列中的对象个数等于0的时候会等待

(1) ArrayBlockingQueue:基于数组的先进先出(IFIO)队列,此队列创建时必须指定大小;队列中的元素

(2) LinkedBlockingQueue:基于链表的先进先出队列,长度不定,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

(3) synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接执行新来的任务。必须消费和生产是交替执行的,就是 put 一个之后必须 take一个, 然后再 put 再take

(4) PriorityBlockingQueue : 这个阻塞队列不是基于先进先出策略的基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,

(5) DelayQueue: 只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞


ThreadLocal类

是一个

维护线程局部变量的容器


用处:保存线程的独立变量。对一个线程类(继承自Thread)

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,是Java中一种较为特殊的线程-变量绑定机制,因此每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突, 常用于用户登录控制,如记录session信息。

实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。

主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。

从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收。


其实ThreadLocal的作用就在于为每一个线程要访问的数据都提供了一份变量的副本,因此可以同时访问而互不影响。



版权声明:本文为woshiluoye9原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。