Java 多线程总结

线程实现方式

  • 继承Thread类创建线类
    Thread本质上是实现了Runnable接口的一个实例

  • 实现Runnable接口
    实现Runnable接口比继承Thread类所具有的优势:适合多个相同的程序代码的线程去处理同一个资源;可以避免java中的单继承的限制

  • 实现Callable接口:
    适用需要线程返回的结果。就要使用用Callable、Future、FutureTask、CompletionService这几个类。Callable只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执行状态。

多线程管理

线程池

  • Executors接口
    提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。有两个重要的实现类。ThreadPoolExecutor:ExecutorService的默认实现;ScheduledThreadPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
  • 固定数目线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
- 优点:创建一个固定大小线程池,超出的线程会在队列中等待。
- 缺点:不支持自定义拒绝策略,大小固定,难以扩展
  • 可缓存的线程池。调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }
- 优点:很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建
- 缺点:一旦线程无限增长,会导致内存溢出。
  • 单线程Executor。
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }
- 优点:创建一个单线程的线程池,保证线程的顺序执行
- 缺点:不适合并发。。不懂为什么这种操作要用线程池。。为什么不直接用队列
  • 支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

public ScheduledThreadPoolExecutor(int corePoolSize,
                             ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
- 优点:创建一个固定大小线程池,可以定时或周期性的执行任务。
- 缺点:任务是单线程方式执行,一旦一个任务失败其他任务也受影响
  • ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

从以上线程池的实现可以看出,本质上他们都是ThreadPoolExecutor类的各种实现版本。

ThreadPoolExecutor

public ThreadPoolExecutor(
        int corePoolSize, - 池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。
        int maximumPoolSize, - 线程池的最大线程数。
        long keepAliveTime, - 当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态
        TimeUnit unit, - keepAliveTime 时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS
        BlockingQueue workQueue, - 用来储存等待执行任务的队列。
                              ThreadFactory threadFactory, - 线程工厂有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。

开发注意事项

  • 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
    1)FixedThreadPool和SingleThreadPool:
      允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
    2)CachedThreadPool:
      允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

总结

你可能感兴趣的:(Java 多线程总结)