java多线程以及Android多线程

Java 多线程

线程和进程的区别

  • 线程和进程的本质:由CPU进行调度的并发式执行任务,多个任务被快速轮换执行,使得宏观上具备多个线程或者进程同时执行的效果。html

  • 进程:在操做系统来讲,一个运行的程序或者说一个动态的指令集合一般对应一个进程Process,它是系统进行资源分配和调度的一个独立单位,也是拥有系统资源的基本单位。进程是系统中独立存在的实体,它能够拥有本身独立的资源,拥有本身私有的地址空间,进程之间不能直接访问其余进程的地址空间。
  • 线程:线程是CPU调度的基本单位,也就是说在一个进程中能够有多个并发程序执行流,线程拓展了进程的概念,使得任务的执行获得更加的细分,因此Thread有时候也被称为Lightweight Process。线程是进程的执行单元,可是线程不是分配系统资源的单位,它们共享所在进程的资源,包括共享内存,公有数据,全局变量,进程文件描述符,进程处理器,进程代码段,进程用户ID等等。
  • 线程独立拥有本身的线程ID,堆栈,程序计数器,局部变量,寄存器组值,优先级,信号屏蔽码,错误返回码等等,线程是独立运行的,其执行是抢占式的。线程共享进程资源,线程之间的通讯要进程之间的通讯来得容易得多。此外,线程的建立和销毁的开销也远远小于进程的系统开销。java

线程和线程池

  • 线程池:虽然线程的建立销毁的开销相对较小,可是频繁得建立和销毁也会消耗有限的资源,从而带来性能上的浪费,也不够高效。所以线程池的出,现就是为了解决这一问题,即在初始状态建立并维护必定数量的空闲线程,当有须要执行的任务,就交付给线程中的一个线程,任务执行结束后,该线程也不会死亡,而是回到线程池中从新变为空闲状态。
  • 线程池的好处:减小线程频繁建立销毁的资源开销,同时可以有效控制系统中并发线程的数量,防止系统性能的剧烈降低。

线程建立/启动的三种方法

  • 继承Thread类建立多线程,此时每次建立的Thread对象并不能共享线程类的实例变量,也就是下面程序中的i
public class FirstThread extends Thread{
    private int i;
    @Override
    public void run() {
        for(i=0;i<10;i++) 
            System.out.println(getName()); // 继承自Thread 
    }
    
    public static void main(String[] args) {    
        new FirstThread().start();
        new FirstThread().start(); // 注意启动线程须要用Start
    }
}
  • 实现Runnable接口建立线程类,Runnable接口是一个函数式接口(可使用Lambda表达式),一般作法是重写接口中的run方法,此时方法体即为线程执行体,使用Runnable接口实现类的实例做为Thread的target来建立Thread对象,此时由于使用一个共同的target线程执行体,多个线程能够共享一个实例变量。
public class SecondThread implements Runnable{
    private int i;
    @Override
    public void run() {
        for(;i<10;i++) {
            System.out.println(Thread.currentThread().getName() + " "+ i);
        }
    }
    
    public static void main(String[] args) {
        SecondThread targetRunnable = new SecondThread();
        new Thread(targetRunnable,"线程1").start();
        new Thread(targetRunnable).start();
    }
}
  • 使用Callable和Future建立线程:Callable相似于Runnable,提供一个Call()方法做为线程执行体,而且能够有返回值,以及抛出异常,那么咱们如何拿到返回值,java提供了future接口,在接口里定义了一些公共方法来控制关联它的Callable任务,而后java还贴心的给了FutureTask类,该类实现了Future接口和Runnable接口,因此FutureTask的实例就能够做为Thread的Target,因此一般作法是建立Callable接口实现类,并对该实现类的实例使用FutureTask来包装。
public class ThridThread {
    public static void main(String[] args) {        
        // lambda 表达式 + functionInterface 类型转换
        // Callbable: 有返回值
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
            int i =0;
            for(;i<100;i++) {
                System.out.println(Thread.currentThread().getName() + " "+ i);
            }
            return i;
    }); 
        new Thread(task,"有返回值的线程").start();
        try {
            System.out.println("子线程的返回值"+task.get());
        }catch(Exception e) {
            e.printStackTrace();
        }
 }
}

线程的生命周期

  • 线程的生命周期包括:新建New,就绪Runnable,运行Running,阻塞Blocked,和死亡Dead 5种状态。
  • 新建和就绪状态:程序使用new关键字以后,该线程就处于新建状态,jvm为其分配内存,并初始化成员变量。程序调用start() 方法以后,该线程就处于就绪状态,jvm为其建立方法调用栈和PC计数器。
  • 运行和阻塞状态:若是就绪状态的线程得到了CPU,那么程序就处于运行状态。
    当发生以下状况时,线程将进入阻塞状态。
    1. 线程调用sleep()方法,主动放弃所占有的CPU资源。
    2. 线程调用了一个阻塞式IO方法,在该方法返回以前,该线程被阻塞
    3. 线程试图得到一个同步监视器,可是该同步监视器被其余线程所持有
    4. 线程等待某个通知notify,notify一般与wait配合使用
    5. 线程调用suspend(),挂起,该方法容易形成死锁,不建议使用。
    当发生以下状况时,线程进入就绪状态,可是线程何时进入运行状态,须要根据系统调度来决定。
    1. sleep()方法的线程通过了指定的sleep的时间
    2. 阻塞式IO方法返回值
    3. 成功得到了同步监视器
    4. 线程得到了其余线程发出的通知,被唤醒
    5. 挂起的线程调用了Resume()方法恢复。

屏幕快照 2017-12-01 上午11.58.30.png

屏幕快照 2017-12-01 下午2.19.31.png

  • 线程死亡:线程执行体执行结束,以及抛出一个未捕获的Exception或Error,或者直接调用stop()方法结束该线程。能够经过线程对象的isAlive()方法,来判断线程对象的状态(新建或者死亡都会返回false)

注意:抢占式策略系统:系统会给每一个执行的线程一个小的时间段来处理任务,当该时间段用完以后,系统会剥夺该线程所占用的资源,让其余线程得到执行的机会。在系统调度时,还会考虑到线程的优先级问题。android

线程控制

  • join()-线程:让一个线程等待另外一个线程,当在某个线程执行流中调用其余线程的join()方法,该线程将被阻塞,知道join线程执行完毕为止。缓存

  • 后台-线程:后台线程又称为Daemon Thread,守护线程,JVM的垃圾回收线程就是典型的后台线程。特征是:若是全部前台线程都死亡,那么后台线程自动死亡。调用Thread对象的setDaemon(true)能够将指定线程设置为后台线程,注意须要在Start()以前调用,主线程默认为前台线程,前台线程建立的子线程默认为前台线程,后台线程建立的子线程默认为后台线程
  • sleep()-线程:sleep(ms)是Thread类的静态方法,让当前线程暂停millis毫秒,并进入阻塞状态,睡眠状态的线程不会释放同步监视器,在此期间该线程不会得到执行的机会。注意使用sleep方法时须要捕捉InterruptedException或者抛出该异常
public class SleepThread {
    public static void main(String[] args) throws Exception{ // 注意异常
        for(int i =0;i<5;i++) {
            System.out.println("当前时间"+new Date());
            Thread.sleep(1000);
        }       
    }
}
  • yield():线程让步,也是Thread的静态方法,使得正在执行的线程暂停,但不会阻塞线程,只是交出CPU的控制权,将线程转为就绪状态,让系统调度器从新调度一次。当某个线程调用yield方法暂停后,只有优先级与当前线程相同,或者优先级比当前线程更高的线程才有可能得到执行机会。安全

  • 改变线程优先级:setPriority(int newPriority),高优先级的线程能得到更多的执行机会。网络

线程同步

  • 线程的同步的意义在于线程安全,也就是说有多个线程并发访问同一个对象,而线程调度的不肯定性可能带来潜在的安全问题。多线程

  • 同步监视器:java多线程引入同步监视器来解决同步问题,任什么时候刻只能有一个线程得到对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对同步监视器的锁定。java容许任何对象做为同步监视器,一般咱们使用可能被并发访问的共享资源做为同步监视器。并发

  • 同步代码块:显式指定同步监视器
public class DrawThread extends Thread {
    private Account account;
    private double drawaccout;
    
    public DrawThread(String name,Account account,double drawaccount) {
        super(name);
        this.account = account;
        this.drawaccout= drawaccount;
    }
    
    public void run() {
        synchronized(account) {
            if(account.getBlance()>=drawaccount) {
                System.out.println(getName()+"取钱成功");
                try {
                    Thread.sleep(1);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 同步方法:隐式指定同步监视器,使用synchronized关键字来修饰某个方法,此时该方法(非static方法)无需显式指定同步监视器,同步监视器默认为this,也就是调用该方法的对象
public synchronized void draw(double amount) {
        ……
    }
  • 同步监视器的释放:线程会在如下几种状况下释放同步监视器的锁定。
    1. 当前线程的同步方法,同步代码块执行结束或者在执行中遇到break,return等终止了代码块的执行
    2. 同步代码块或者方法中出现未处理的Error或者Exception,致使异常结束
    3. **当前线程执行同步代码块或者同步方法时,程序中执行了同步监视器的wait()方法,wait是object的方法,范围是该object实例所在的线程
  • 同步锁:lock,更增强大的线程同步机制,经过显式定义锁对象来实现同步,也就是Lock对象,线程在访问共享资源以前,须要先得到锁对象。线程安全控制中比较经常使用的是ReetrantLock可重入锁。一个线程能够对已经加锁的ReetrantLock再度加锁。
class X{
        private final ReentrantLock lock = new ReentrantLock();
        
        //须要定义线程安全的方法
        public void foo() {
            lock.lock();//加锁
            try {
                // 须要保证线程安全的代码
            }
            finally {
                lock.unlock();//使用finally块保证释放锁
            }
        }
    }
  • 死锁的问题:
    产生死锁的四个必要条件:
    (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
    (3) 不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。
    (4) 循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。app

    在系统中已经出现死锁后,应该及时检测到死锁的发生,并采起适当的措施来解除死锁。目前处理死锁的方法可归结为如下四种:
    1) 预防死锁:这是一种较简单和直观的事先预防的方法。方法是经过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被普遍使用。可是因为所施加的限制条件每每太严格,可能会致使系统资源利用率和系统吞吐量下降。jvm

    2) 避免死锁:该方法一样是属于事先预防的策略,但它并不须事先采起各类限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程当中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。

    3) 检测死锁:这种方法并不须事先采起任何限制性措施,也没必要检查系统是否已经进入不安全区,此方法容许系统在运行过程当中发生死锁。但可经过系统所设置的检测机构,及时地检测出死锁的发生,并精确地肯定与死锁有关的进程和资源,而后采起适当措施,从系统中将已发生的死锁清除掉。

    4) 解除死锁:这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。经常使用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统得到较好的资源利用率和吞吐量,但在实现上难度也最大。

    java中应该避免死锁的出现。

线程安全是以牺牲程序运行效率为代价的,所以在注意线程安全的同时,也要注意不要滥用锁和同步方法,尽可能只对那些会改变竞争资源的方法进行同步。同时还要根据单线程和多线程运行环境来提供线程不安全和线程安全两种版本,JDK提供的StringBuilder,StringBuffer就是一个例子。

线程通讯

  • Object类提供的wait(),notify(),notifyAll()三个方法,由同步监视器来调用,对于同步方法,其同步监视器是默认实例this,能够再同步方法中直接调用这三个方法。
    1. wait(): 当前线程等待或者等待若干ms,当前线程自动释放同步监视器,线程进入等待状态(阻塞),直到其余线程调用了该同步监视器的notify()或者notifyAll方法。
    2. notify():唤醒在同步监视器上等待的单个线程,如有多个线程等待,则任意选择其中一个。
    3. notifyAll():唤醒在此同步监视器上等待的全部线程。
  • 使用Condition控制线程通讯:使用Lock对象来保证同步问题时,咱们可使用Condition类来释放Lock以及唤醒其余等待线程。
private final Lock lock = new ReentrantLock();
    // Condition实例绑定在一个Lock对象上
    private final Condition cond = lock.newCondition();
    
    public void Draw(double drawamount) {
        lock.lock();
        try {
            if(!flag)
                cond.await();//致使当前线程等待
            else {
                // ...
                cond.signalAll();// 唤醒其余线程
            }
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
    }
  • 使用阻塞队列(BlockingQueue)控制线程通讯:当生产者线程试图向Blocking Queue中放入元素时,若是队列已满,则该线程被阻塞,当消费者线程试图从BlockingQueue中取出元素时,若是队列已空,则该线程阻塞。

线程池

  • 使用线程池执行线程任务的步骤是:
    1. 调用Executors类的静态工厂方法建立一个ExecutorService对象,该对象表明一个线程池。
    2. 建立Runnable实现类或者Callable实现类的实例,做为线程的执行任务。
    3. 调用ExecutorService对象的submit方法来提交Runnable或者Callable实例。
    4. 当没有任务时,使用shutdown()方法来关闭线程池。
    public class Testjava{
      public static void main(String[] args)
      throws Exception{
          ExecutorService pool = Executors.newFixedThreadPool(6);
          Runnable target = ()->{
              for(int i=0;i<100;i++) {
              System.out.println(Thread.currentThread().getName()
                          + "的i值为:"+ i);
              }
          };      
          // 向线程池中提交两个线程
          pool.submit(target);
          pool.submit(target);        
          pool.shutdown();
      }
    }
  • Executors是一个工厂类,它包含了以下几个静态工厂方法来建立线程池。
    20150827155651746.png
    1. newCachedThreadPool():建立一个具备缓存功能的线程池,系统根据须要建立线程
    2. newFixedThreadPool(int nThreads):建立一个可重用的,具备固定线程数的线程池
    3. newSingleThreadExecutor():建立一个单线程线程池
    4. newScheduledThreadPool(int corePoolSize):建立具备指定线程数的线程池,它将在指定延迟后执行线程任务
    5. newSingleThreadScheduledExecutor():建立一个延迟执行的单线程线程池
    6. newWorkingStealingPool(int parallelism):建立持有足够的线程的线程池来支持给定的并行级别,以充分支持多CPU并行能力。
    7. newWorkingStealingPool():根据CPU个数设置并行级别。
    12367 返回ExecutorService对象,表明一个线程池,能够执行Runnable以及Callable对象所表明的线程,45返回的ScheduledExecutorService对象。

【Java8源码分析】线程池-Executor与ExecutorService的全面剖析

线程相关类

  • ThreadLocal:Thread Local Variable线程局部变量,为每一个使用该变量的线程提供一个变量值的副本,从而隔离多线程程序的竞争资源。ThreadLocal类的用法很简单,它提供了三个public方法:
    1. T get():返回此线程局部变量当前线程副本的值
    2. void remove():删除此线程局部变量中当前线程的值
    3. void set(T value):设置副本值
    class Accout{
          private ThreadLocal<String> name = new ThreadLocal<>();
          public Accout(String str) {
              this.name.set(str);
          }
          public String getname() {
              return name.get();
          }
          public void setname(String str) {
              this.name.set(str);
          }
      }

    注意:ThreadLocal与其余同步机制都是为了解决访问同一资源冲突问题而出现的,可是侧重的领域不一样,同步机制为实现多个线程对相同资源访问的并发安全性,ThreadLocal则是隔离多个线程之间的数据共享,从而避免竞争。

  • 线程安全的集合类:
    1. ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap都是线程不安全的,若是须要在多线程中对上述集合类进行存取,须要使用Collections提供的静态方法将其包装成线程安全的类。
    2. 更好的方法是使用 java.util.concurrent包下提供的大量支持高效并发访问的集合接口和实现类。如ConcurrentHashMap,ConcurrentLinkedQueue,ConcurrentLinkedDeque

Android多线程

Android中的多线程本质上也是Java的多线程,同时添加了一些不一样的特性和使用的场景。其中,最主要的一个区别就是Android中主线程和子线程中的区分,Android中的主线程是UI线程,负责运行四大组件并与用户实现交互,须要保持较高的反应速度,因此主线程不容许进行耗时的操做(好比说网络请求和访问),不然容易出现ANR现象,子线程则负责处理 一些耗时的任务,而若是子线程中想要实现对UI的操做,则须要经过Android的handler消息机制。

为何子线程中不容许对UI进行操做呢
由于Android的UI控件并非线程安全,多线程的并发访问会带来UI控件的不可预期的状态,且考虑到加锁机制会带来性能上的问题,所以Android在设计初期就禁止子线程处理UI。UI操做时ViewRootImpl会对操做者所在的线程进行checkThread,若是非主线程,会抛出CalledFromWrongThreadException。

那么Android除了java原生的Thread/Runnable等线程形态,还有哪些包装过了的有特色的线程形式?

  • AsyncTask: AsyncTask 封装了线程池和Handler,主要是为了方便开发者不去写本身的后台线程和定义Handler,而方便更新UI界面。
  • IntentService: IntentService从名字来看,能够知道它是一个服务,其内部采用HandlerThread执行任务,执行完毕后自动退出。其特色是它是Service,比起其余线程来讲具备更高的优先级,不容易被系统杀死,而可以保证任务的执行。
  • HandlerThread: HandlerThread是一个具备消息循环loop的线程,也就是一开始就准备好了loop的线程,在其内部能够直接使用Handler。

AsyncTask

很棒的参考:你真的了解AsyncTask

  • AsyncTask 是一个轻量级的一部任务类,在线程池中执行后台任务。而后将任务的进度和最终结果传递给主线程,并在主线程中更新UI。

  • AsyncTask是一个抽象的泛型类,使用时必须继承并实现它的子类,而且至少重写doInBackground(Params...)方法。
// 三个泛型参数 不须要传递参数时,能够用void代替
public abstract class AsyncTask<Params,Progress,Result>
  • AsyncTask的使用
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    //在线程池中执行 该方法必须返回计算结果给onPostExecute()
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            // 可使用该方法返回任务的进度,该方法会调用onProgressUpdate()
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
    // 被主线程调用执行 所以这里能够有UI操做
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    // 在主线程中调用执行 任务执行结束后,会调用该方法,所以这里能够有UI操做
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}
// 主线程调用execute方法,执行任务前,会调用[1] onPreExecute()完成一些准备工做
// onPreExecute()是在主线程中执行
new DownloadFilesTask().execute(url1, url2, url3);
// 也能够调用cancel来取消任务的执行
  • AsyncTask的使用注意事项:
    1. AsyncTask的类必须在主线程中加载,这一点液晶在Android4.1以上版本上自动完成
    2. AsyncTask 对象必须在主线程中建立
    3. execute方法必须在主线程中调用
    4. 不要直接调用 onPreExecute(),onPostExecute(Result)doInBackground(Params...), onProgressUpdate(Progress...)
    5. 一个AsyncTask对象只能执行一次,不然会运行报错。
  • AsyncTask实现原理:
    1. AsyncTask中有两个线程池:SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR用于真正执行任务,定义了一个InternalHandler用于对UI进行操做,该handler是一个静态的Handler对象,因此想要对UI进行操做,这个handler对象必须在UI线程中建立,也就是为何AsyncTask类为何要在UI线程加载。
    2. ThreadPoolExecutor是真正创建线程池的类,它在AsyncTask类中建立线程池的参数以下:
    public abstract class AsyncTask<Params, Progress, Result> {
      private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU数
      private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
      private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
      private static final int KEEP_ALIVE = 1;
    
      private static final ThreadFactory sThreadFactory = new ThreadFactory() {
          private final AtomicInteger mCount = new AtomicInteger(1);
    
          public Thread newThread(Runnable r) {
              return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
          }
      };
    
      private static final BlockingQueue<Runnable> sPoolWorkQueue =
              new LinkedBlockingQueue<Runnable>(128);
    
      public static final Executor THREAD_POOL_EXECUTOR
              = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                      TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    }
    // 核心线程数 = CPU数+1
    // 最大线程数 = CPU数*2 + 1
    // 非核心线程的超时时间为1秒
    // 任务队列的容量为128

Android 开发手册写明:AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by thejava.util.concurrentpackage such as Executor,ThreadPoolExecutorand FutureTask

HandlerThread

  • HandlerThread 是Thread的子类,它的特色是可使用Handler的Thread。它的run方法体以下所示,外界能够经过handler来通知HandlerThread来处理某些事情,从这能够看出Handler的用处不只仅局限于处理UI相关的问题。
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized(this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid =-1;   
}

IntentService

  • IntentService封装了HandlerThread和Handler,可是它继承了Service,因此致使它的优先级比单纯线程要高,因此IntentService适合执行一些高优先级的后台任务。

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory,
                      RejectedExecutionHandler handler) {
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • corePoolSize: 线程池的核心线程数,默认状况下, 核心线程会在线程池中一直存活, 即便处于闲置状态. 但若是将allowCoreThreadTimeOut设置为true的话, 那么核心线程也会有超时机制, 在keepAliveTime设置的时间事后, 核心线程也会被终止.
  • maximumPoolSize: 最大的线程数, 包括核心线程, 也包括非核心线程, 在线程数达到这个值后,新来的任务将会被阻塞.
  • keepAliveTime: 超时的时间, 闲置的非核心线程超过这个时长,讲会被销毁回收, 当allowCoreThreadTimeOut为true时,这个值也做用于核心线程.
  • unit:超时时间的时间单位.
  • workQueue:线程池的任务队列, 经过execute方法提交的runnable对象会存储在这个队列中.
  • threadFactory: 线程工厂, 为线程池提供建立新线程的功能.
  • handler: 任务没法执行时,回调handler的rejectedExecution方法来通知调用者.
  1. 若是线程池中线程的数目少于corePoolSize,就算线程池中有其余的没事作的核心线程,线程池仍是会从新建立一个核心线程;直到核心线程数目到达corePoolSize(常驻线程就位)

  2. 若是线程池中线程的数目大于或者等于corePoolSize,可是工做队列workQueue没有满,那么新的任务会放在队列workQueue中,按照FIFO的原则依次等待执行;(当有核心线程处理完任务空闲出来后,会检查这个工做队列而后取出任务默默执行去)
  3. 若是线程池中线程数目大于等于corePoolSize,而且工做队列workQueue满了,可是总线程数目小于maximumPoolSize,那么直接建立一个线程处理被添加的任务。
  4. 若是工做队列满了,而且线程池中线程的数目到达了最大数目maximumPoolSize,那么就会用最后一个构造参数handler处理;**默认的处理方式是直接丢掉任务,而后抛出一个异常。

相关文章
相关标签/搜索