java 多线程

3.1.1、线程安全

​ 多个线程访问某个类时,不管运行环境和调度方式,这个类始终表现出正确的行为,称为线程安全 。

3.1.2、多线程编程3个核心概念

  1. 原子性:同数据库,要么全执行,要么全失败。(锁和同步)

  2. 可见性:并发访问共享变量时,对共享变量的修改,其他都能看到。(volatile修饰)

  3. 顺序性:jvm优化,调整顺序代码,程序的执行顺序并非按照代码的先后顺序。

3.1.3、线程之间的状态

 

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3.1.3.2、线程阻塞情况

线程阻塞 描述
1.等待阻塞 运行的线程执行wait()方法,会释占有的资源,jvm会将线程放入“等待池中”。不能自动唤醒,必须依靠其他线程调用notify()/notifyall()
2.同步阻塞 运行的线程在获取对象的同步锁时,若该同步锁被其他线程占用,jvm会把该线程放入“锁池中”
3.其他阻塞 运行的线程执行sleep()/join()方法,或者发出IO请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时,join()等待线程终止或者超时,或者IO处理完毕时,线程重新转入就绪状态。

 

3.1.4、java四种线程池简介

四种线程池内部构造都是来自同一个方法:

public ThreadPoolExecutor(int corePoolSize,  //核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//存活时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列
                              ThreadFactory threadFactory) { //拒绝策略
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

BlockingQueue: 任务队列,如果当前线程池中核心线程数达到了corePoolSize时,且当前所有线程都属于活动状态时,则将新的任务添加到该队列中。基本上有以下几个实现: 1) ArrayBlockQueue:基于数组结构的有界队列,此队列按FIFO(first in first out)原则对任务进行排序。如果队列已满,新的任务将会被采取拒绝策略对待。 2)LinkedBlockingQueue: 基于链表的无界队列,按FIFO原则排序。因为是无界的,所以不存在满的情况,此时拒绝策略无效 3) PriorityBlockingQueue:具有优先级的队列的有界队列,可以自定义优先级,默认为自然排序。

Handler:拒绝策略,当线程池和workQueue都满了的情况下,对新任务采取的处理策略,有四种默认实现: 1) AbortPolicy:拒绝任务,且还抛出RejectedExecutionException异常,线程池默认策略 2) CallerRunPolicy:拒绝新任务进入,如果该线程池还没有被关闭,那么这个新的任务在执行线程中被调用 3) DiscardOldestPolicy: 如果执行程序尚未关闭,则位于头部的任务将会被移除,然后重试执行任务(再次失败,则重复该过程),这样将会导致新的任务将会被执行,而先前的任务将会被移除。 4)DiscardPolicy:没有添加进去的任务将会被抛弃,也不抛出异常。基本上为静默模式。

3.1.4.1、fixThreadPool  正规线程

​ 它是一种线程数量固定的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新的任务都会处于等待状态,直到有线程空闲出来。FixedThreadPool只有核心线程,且该核心线程都不会被回收,这意味着它可以更快地响应外界的请求。jdk实现如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
​

3.1.4.2、caCheThreadPool  缓存线程池

​ 只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

3.1.4.3、singleThreadPoll   单线程线程池

​ 看这个名字就知道这个家伙是只有一个核心线程,就是一个孤家寡人,通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。缺点就是一个人干活效率慢。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
​

3.1.4.4、ScheduledThreadPoll

  这个线程池就厉害了,是唯一一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收 。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

注意:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章