线程池

最近项目组组织学习线程池,也受到了打击,以后坚持做笔记不偷懒了。

要了解线程池先了解几个相关的概念

线程:进程中负责执行的单元

进程:进程有独立的代码和数据空间(一个app),一个进程可以有多个线程

多线程:解决多任务执行的需求,合理使用cpu的资源,线程的切换由cpu决定,因此那个线程在执行具有不确定性

线程池:思想就是开辟出一个对象池的思想,就是开辟出一块内存空间,存放很多未死的线程,池中线程的调度由池管理器来管理,当有线程任务时从池中取出线程执行,执行完成后线程对象放回池中,这样可以避免反复创建线程对象带来的性能开销,节约了系统资源。

创建线程的三种方式

1、继承Thread类,覆盖run()方法。创建线程对象并用start()方法启动线程。

2、实现Runnable接口来创建Thread线程 run();

3、通过实现Callable接口来创建Thread线程 call()

      Thread 类中的 start() 和 run() 方法,start会重新启动线程

     系统接口HandlerThread 继承了Thread,他是可以使用Handler的Thread,一个具有消息循环的线程。            run()方法中通过Looper.prepare()来创建消息队列,通过Looper.loop(),来开启消息循环,在run()

      方法中执行耗时操作,Handler内部创建了一个消息队列,外部需要Handler的方式通知

       HandlerThread执行具体的任务,通过quite()/quitSafely()方法来终止线程的执行。


      在选择使用继承Thread还是实现Runnable时JAVA类可以多实现却不可以多继承根据情况做选择。

      与Runnable的主要区别是 Callable 的 call() 方法可以返回值和抛出异常,而 Runnable 的 run() 方法没       有这些功能。Callable 可以返回装载有计算结果的 Future 对象。

     Callable无法在新线程中new Thread(Runnable r)使用,Thread 类只支持 Runnable不过 Callable 可以       使用 ExecutorService 。

Callable创建线程的两种方式

1、通过实现Callable接口来创建Thread线程:

步骤1:创建实现Callable接口的类SomeCallable;

步骤2:创建一个类对象:

Callable oneCallable = new SomeCallable();

步骤3:由Callable创建一个FutureTask对象:

FutureTask oneTask = new FutureTask(oneCallable);

FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口,他的run方法中实际上会调用oneCallable.call()。

步骤4:由FutureTask创建一个Thread对象:

Thread oneThread = new Thread(oneTask);

步骤5:启动线程:

oneThread.start();

事例:

Callable callable =newCallable() {   

 @Override

publicStringcall() throws Exception {

return"哈哈哈";  

  }};

FutureTask task =newFutureTask(callable);

Thread t =newThread(task);t.start();// 启动线程t

ask.cancel(true);// 取消线程

2、通过线程池来创建线程:

步骤1:创建线程池:

ExecutorService pool = Executors.newCachedThreadPool();

步骤2:通过Runnable对象或Callable对象将任务提交给ExecutorService对象:

Future future = pool.submit(new Callable() );。

Future

该接口定义5个方法

boolean cancel(boolean mayInterruptIfRunning):试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel() 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。此方法返回后,对 isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 isCancelled() 的后续调用将始终返回 true。

boolean isCancelled():如果在任务正常完成前将其取消,则返回 true。

boolean isDone():如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。

V get()throws InterruptedException,ExecutionException:如有必要,等待计算完成,然后获取其结果。

V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)

FutureTask

FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

public classFutureTaskimplementsRunnableFuture {

.........

}

run()方法调用了callable中的call()

importjava.util.concurrent.*;

publicclassTest {

public static void main(String[] args) throwsInterruptedException,

ExecutionException {

finalExecutorService exec = Executors.newFixedThreadPool(5);

Callable call =newCallable() {

publicString call()throwsException {

Thread.sleep(1000*10);//休眠指定的时间,此处表示该操作比较耗时

return"Other less important but longtime things.";

}

};

Future task = exec.submit(call);

//重要的事情

System.out.println("Let's do important things. start");

Thread.sleep(1000*3);

System.out.println("Let's do important things. end");

//不重要的事情

while(! task.isDone()){

System.out.println("still waiting....");

Thread.sleep(1000*1);

}

System.out.println("get sth....");

String obj = task.get();

System.out.println(obj);

//关闭线程池

exec.shutdown();

}

}

输出结果:

Let's do important things. start

Let's do important things. end

still waiting....

still waiting....

still waiting....

still waiting....

still waiting....

still waiting....

still waiting....

get sth....

Other less important but longtime things.

多线程的特点

1)适当的提高程序的执行效率(多个线程同时执行)。

2)适当的提高了资源利用率(CPU、内存等)。

1)占用一定的内存空间。

2)线程越多CPU的调度开销越大。

3)程序的复杂度会上升。

synchronized

修饰一个代码块,修饰一个方法,修改一个静态的方法,修饰一个类。

wait(),notify(),notifyAll(),调用这三个方法中任意一个,当前线程必须是锁的持有者,如果不是会抛出一个 IllegalMonitorStateException 异常。

wait() 方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被 notify() 方法通知或者 wait() 的等待的时间到。

sleep() 方法使持有的线程暂停运行,从而使线程进入休眠状态,直到用 interrupt 方法来打断他的休眠或者 sleep 的休眠的时间到。

wait() 方法进入等待状态时会释放同步锁,而 sleep() 方法不会释放同步锁。

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

Thread.yield():线程放弃运行,将CPU的控制权让出。


线程池

1)避免线程的创建和销毁带来的性能开销。

2)避免大量的线程间因互相抢占系统资源导致的阻塞现象。

3}能够对线程进行简单的管理并提供定时执行、间隔执行等功能。

Java里面线程池的顶级接口是 Executor,不过真正的线程池接口是 ExecutorService, ExecutorService 的默认实现是 ThreadPoolExecutor;

普通类 Executors 里面调用的就是 ThreadPoolExecutor。

publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,                      TimeUnit unit, BlockingQueue workQueue,   ThreadFactory threadFactory){

this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);}

1)corePoolSize:线程池的核心线程数,一般情况下不管有没有任务都会一直在线程池中一直存活,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true 时,闲置的核心线程会存在超时机制,如果在指定时间没有新任务来时,核心线程也会被终止,而这个时间间隔由第3个属性 keepAliveTime 指定。

2)maximumPoolSize:线程池所能容纳的最大线程数,当活动的线程数达到这个值后,后续的新任务将会被阻塞。

3)keepAliveTime:控制线程闲置时的超时时长,超过则终止该线程。一般情况下用于非核心线程,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true时,也作用于核心线程。

4)unit:用于指定 keepAliveTime 参数的时间单位,TimeUnit 是个 enum 枚举类型,常用的有:TimeUnit.HOURS(小时)、TimeUnit.MINUTES(分钟)、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。

5)workQueue:线程池的任务队列,通过线程池的 execute(Runnable command) 方法会将任务 Runnable 存储在队列中。

6)threadFactory:线程工厂,它是一个接口,用来为线程池创建新线程的。

Executors提供四种线程池

newCachedThreadPool 可变尺寸的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute() 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。(只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务)

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,所有的任务是串行执行的,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,保证所有任务按照指定顺序(FIFO)执行。(只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。)

newFixedThreadPool 固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小,线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。(只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。)

newScheduledThreadPool 创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。

线程池 submit 和 execute

接收的参数不一样

submit有返回值,而execute没有

线程池的关闭

ThreadPoolExecutor 提供了两个方法,用于线程池的关闭。

shutdown():不会立即的终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。

shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容