Java多线程理解

  • 1.概念
  • 2.多线程的作用
  • 3. 创建线程的方式
  • 4.start()方法和run()方法的区别
  • 5.Runnable接口和Callable接口的区别
  • 6.线程池
  • 1)自定义线程池的创建
  • 2)线程池参数解释:
  • 3)四种常用的线程池
  • 1. newCachedThreadPool
  • 2.newFixedThreadPool
  • 3.newSingleThreadExecutor
  • 4.newScheduleThreadPool
  • 2)工作过程
  • 3)线程池工作队列
  • 4)线程池拒绝策略
  • 7.未完待续...


1.概念

2.多线程的作用

  1. 发挥多核CPU的优势。**单核CPU上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。**现在的计算机都是双核的,4核、8核甚至16核的,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。
  2. 防止阻塞。从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。而单核CPU应用多线程,就是为了防止阻塞,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
  3. 便于建模。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。

3. 创建线程的方式

 

4.start()方法和run()方法的区别

  1. 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
  2. run: run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
  3. 总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。

5.Runnable接口和Callable接口的区别

  1. Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  2. Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

6.线程池

1)自定义线程池的创建

ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,BlockingQueue<Runnable>,ThreadFactory,RejectedExecutionHandler)

2)线程池参数解释:

 

3)四种常用的线程池

1. newCachedThreadPool

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

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

特点:

  • 配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一个无容量的阻塞队列 SynchronousQueue,因此任务提交之后,将会创建新的线程执行;线程空闲超过60s将会销毁

2.newFixedThreadPool

解释: 创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

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

特点:

  • 使用了一个无界LinkedBlockingQueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由RejectedExecutionHandler处理

3.newSingleThreadExecutor

解释: 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。

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

特点:

  • 无界阻塞队列LinkedBlockingQueue;保证任务由一个线程串行执行

4.newScheduleThreadPool

解释:创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

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

特点: 无界延迟阻塞队列DelayedWorkQueue。

2)工作过程

Java多线程与线程池使用 java多线程原理作用_Java多线程与线程池使用

3)线程池工作队列

(1)ArrayBlockingQueue:规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。

(2)LinkedBlockingQueue:大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。

(3)PriorityBlockingQueue:类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。

(4)SynchronizedQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成。

4)线程池拒绝策略

  • AbortPolicy
    简单粗暴,直接抛出拒绝异常,这也是默认的拒绝策略。
  • CallerRunsPolicy
    如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。
  • DiscardPolicy
    从方法看没做任务操作,即表示不处理新任务,即丢弃。
  • DiscardOldestPolicy
    抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。

7.未完待续…