2017.12.04 多线程

来源:互联网 发布:侠客风云传有mac版吗 编辑:程序博客网 时间:2024/05/21 12:39

一.多线程
多线程目的:更好地利用CPU的资源。
1.基本概念
(1)多线程:一个程序(进程)运行时产生了不知一个线程。
(2)并行与并发
并行:多个CPU实例或者多台机器同时执行一段处理逻辑,时真正的同时。
并发:通过CPU调度算法,让用户看上去同时执行,实际从CPU操作层面并不是同时执行。
(3)线程安全:经常用来描绘一段代码。指在并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。
(4)同步:通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果准确。
线程安全的优先级高于性能。

2.线程的状态
线程的状态

线程在Running的过程中可能会遇到阻塞(Blocked)情况
(1)调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
(2)调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
(3)对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

3.每个对象都有的方法
monitor:每个对象都有一个监视器。
synchronized、wait、notify针对同一个监视器。意味着wait之后,其他线程可以进入同步块执行。
synchronized:
(1)对于并发工作,需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它了。
(2)如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。
注意:每个访问临界共享资源的方法都必须被同步,否则它们不会正确工作。
(3)synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

volatile

4.基本线程类
(1)
public class MyThread extends Thread{
public void run(){
//do work
}
}

(2)
public class MyThread implements Runnable{
public void run(){
//do work
}
}

Thread thread = new MyThread();
thread.start();

中断:中断是一个状态,需要去检测。

5.高级多线程控制类

二.线程池
核心类:ThreadPoolExecutor
线程复用。
1.参数含义:
corePoolSize:核心池的大小。
maximumPoolSize:线程池最大线程数。
注:在线程数大于核心线程池大小(corePoolSize),且任务缓存队列满了后,会尝试创建新的线程去执行这个任务。但是当线程数达到最大线程数(maximumPoolSize)时,会按照拒绝任务策略来处理任务。
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况只有线程数大于corePoolSize才起作用。
unit:参数keepAliveTime的时间单位。
workQueue:一个阻塞队列,用来存储等待执行的任务。
通常有三种类型:
①ArrayBlockingQueue:基于数组、先进先出、创建时指定大小。
②LinkedBlockingQueue:基于链表、先进先出、默认Integer.MAX_VALUE。
③synchronousQueue:直接创建线程来执行新来的任务。
threadFactory:线程工厂,主要用来创建线程。
handler:表示当拒绝处理任务时的策略,有以下四种取值。

2.重要方法
execute() 向线程池提交一个任务,交由线程池执行。
submit() 同上,但可以返回任务执行结果。
shutdown() 关闭线程池
shutdownNow() 关闭线程池

3.任务提交后的处理策略
①如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
②如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
③如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
④如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

4.任务拒绝策略
条件:任务缓存队列满 && 线程数大于maximumPoolSize
常用四种:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

5.线程池关闭:
①shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
②shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

6.使用示例

7.配置线程池大小
如果是CPU密集型任务(计算),就需要尽量压榨CPU,参考值可以设为 NCPU+1
如果是IO密集型任务(文件读写),参考值可以设置为2*NCPU

来源参考http://www.importnew.com/19011.html#comment-578619

原创粉丝点击