Java多线程的4种实现方式

Java有四种实现多线程的方式:

1).继承Thread类

2).实现Runnable接口

3).实现Callable接口

4).使用线程池

前三种实现方式的代码:

 1 public class MultiThread {
 2   //继承Thread类
 3     static class MyThread1 extends Thread{
 4         @Override
 5         public void run() {
 6             System.out.println(Thread.currentThread().getName()+": extends Thread方式");
 7         }
 8     }
 9   //实现Runnable接口
10     static class MyThread2 implements Runnable{
11         @Override
12         public void run() {
13             System.out.println(Thread.currentThread().getName()+": implements Runnable方式");
14         }
15     }
16   //实现Callable接口
17     static class MyThread3 implements Callable<String>{
18         @Override
19         public String call() throws Exception {
20             return Thread.currentThread().getName()+": implements Callable<V>方式";
21         }
22     }
23 
24     public static void main(String[] args) throws Exception{
25         Thread thread1 = new MyThread1();
26         Thread thread2 = new Thread(new MyThread2());
27         FutureTask<String> futureTask = new FutureTask<>(new MyThread3());
28         Thread thread3 = new Thread(futureTask);
29 
30         thread1.start();
31         thread2.start();
32         thread3.start();
33         System.out.println(futureTask.get());
34     }
35 }
运行结果:
Thread-0: extends Thread方式 Thread-1: implements Runnable方式 Thread-2: implements Callable<V>方式 

line26,27行当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考Thread类部分源代码:

public class Thread implements Runnable {
    public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
  @Override
  public void run() {
   if (target != null) {
  target.run();
  }
  }
}

1.继承Thread本质上也是实现Runnable接口。

2.实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的线程不能返回结果

3.使用Callable的方式时候需要用FutureTask<>进行包装(适配),FutureTask实现了Runnable和Future,通过future里面的get()方法来得到返回值。需要注意的是get()方法是阻塞的,就是说取不到值会使主线程一直等待。

4.生产中几乎都会使用线程池。

线程池:用来控制运行的线程的数量,处理过程中将任务放入等待队列,然后在线程创建后启动这些任务。如果线程数量超过了最大数量(maximumPoolSize),超出数量的线程将会在等待队列排队等候。如果等待队列已满,再进来的任务就会按照拒绝策略拒绝。

线程池的特点:线程复用,控制最大并发数,管理线程。

线程池的优势:

1.降低资源消耗,通过复用线程来降低创建和销毁线程的消耗。

2.提高响应速度,当任务到达时不需要等待创建线程。

3.提高线程的可管理性,使用线程池可以进行统一的分配,监控和调优。

java自带的线程池的实现:

     //固定数量线程的线程池
        ExecutorService threadPool1 = Executors.newFixedThreadPool(cpuNum);
        //一个线程的线程池
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //多个线程的线程池
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        //Java8新特性
     ExecutorService threadPool3 = Executors.newWorkStealingPool();

上面几线程池的底层都是ThreadPoolExecutor(),ThreadPoolExecutor是线程池的核心类。ThreadPoolExecutor的构造器最多有7个可配参数:

ThreadPoolExecutor的7个参数:

  • corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  • maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
  • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
  • unit:参数keepAliveTime的时间单位,有7种取值:
    TimeUnit.DAYS;//天
    TimeUnit.HOURS;//小时
    TimeUnit.MINUTES;//分钟
    TimeUnit.SECONDS;//秒
    TimeUnit.MILLISECONDS;//毫秒
    TimeUnit.MICROSECONDS;//微妙
    TimeUnit.NANOSECONDS;//纳秒
  • workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
  • threadFactory:线程工厂,主要用来创建线程;
  • handler:表示当拒绝处理任务时的策略,有以下四种取值:
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

线程池详细原理可以参考:https://www.cnblogs.com/dolphin0520/p/3932921.html

 

posted @ 2019-05-05 23:06  有梦想的人不睡觉s  阅读(841)  评论(0编辑  收藏  举报