在前面一篇介绍了线程的生命周期【并发编程之多线程概念】,在本篇将正式介绍如何创建、中断线程,以及线程是如何销毁的。最后,我们会讲解一些常见的线程API。

线程创建

Java 5 以前,实现线程有两种方式:扩展java.lang.Thread类,实现java.lang.Runnable接口。这两种方式都是都是直接创建线程,而每次new Thread都会消耗比较大的资源,导致每次新建对象时性能差;而且线程缺乏统一管理,可能无限制新建线程,相互之间竞争,很可能占用过多系统资源导致死机或OOM。同时,new Thread的线程缺乏更多功能,如定时执行、定期执行、线程中断。

Java 5开始,JDK提供了4中线程池(newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newSingleThreadExecutor)来获取线程。这样做的好处是:可以重用已经存在的线程,减少对象创建、消亡的开销,性能佳;而且线程池可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。通过特定的线程池也可以实现定时执行、定期执行、单线程、并发数控制等功能。

创建线程的代码实现

扩展java.lang.Thread类

自定义一个类继承java.lang.Thread

重写Thread的run(),把自定义线程的任务定义在run方法上

实例化自定义的Thread对象,并调用start()启动线程

//1.自定义一个类继承Thread类

public class ExThread extendsThread {//2.重写run()

@Overridepublic voidrun() {for (int i = 0; i < 100; i++) {

System.out.println(Thread.currentThread().getName()+”:”+i);

}

}public static voidmain(String[] args) {//3.创建Thread子类对象

ExThread exThread = newExThread();//4.调用start方法启动自定义线程

exThread.start();

}

}

实现java.lang.Runnable接口

自定义一个类实现Runnable接口

实现Runnable接口中的run(),把自定义线程的任务定义在run方法上

创建Runnable实现类的对象

创建Thread对象,并且把Runnable实现类的对象作为参数传递

调用Thread对象的start()启动自定义线程

//1.自定义一个类实现Runnable接口

public class ImThread implementsRunnable{//2.实现run()

@Overridepublic voidrun() {for (int i = 0; i < 100; i++) {

System.out.println(Thread.currentThread().getName()+”:”+i);

}

}public static voidmain(String[] args) {//3.创建Runnable实现类对象

ImThread imThread = newImThread();//4.创建Thread对象

Thread thread = newThread(imThread);//5.调用start()开启线程

thread.start();

}

}

newFixedThreadPool

创建一个固定线程数的线程池,可控制线程最大并发数,超出的线程会在队列中等待

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;public classCreateThreadByFixedPool {/*** Cover Runnable.run()*/

private static voidrun(){

System.out.println(Thread.currentThread().getName()+" is running...");try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public static voidmain(String[] args) {

ExecutorService pool= Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {

pool.execute(CreateThreadByFixedPool::run);

}

}

}

newCachedThreadPool

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

线程池的容量为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;public classCreateThreadByCachedPool {public static voidmain(String[] args) {

ExecutorService pool=Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

pool.execute(()-> System.out.println(Thread.currentThread().getName()+" is running..."));

}

}

}

newScheduledThreadPool

创建一个固定线程数的线程池,支持定时及周期性任务执行。

importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;public classCreateThreadByScheduledPool {public static voidmain(String[] args) {

ScheduledExecutorService pool= Executors.newScheduledThreadPool(3);//delay 2s excute.

pool.schedule(() -> System.out.println(Thread.currentThread().getName()+" delays 2s "),2, TimeUnit.SECONDS);//delay 2s and every 3s excute.

pool.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName()+" delays 2s every 3s execte"),2, 3, TimeUnit.SECONDS);

}

}

newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public classCreateThreadBySingleThreadPool {public static voidmain(String[] args) {

ExecutorService pool=Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index =i;

pool.execute(()->{

System.out.println(String.format("The thread %d (%s) is running...",

index,Thread.currentThread().getName()));try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

});

}

}

}

Thread负责线程本身相关的职责和控制,Runnable负责逻辑业务。

在实现自定义线程时,推荐使用Runnable接口,因为其具有以下几个优点:

Java是单继承多实现的,实现接口有利于程序拓展

实现Runnable接口可为多个线程共享run() 【继承Thread类,重写run()只能被该线程使用】

不同线程使用同一个Runnable,不用共享资源

线程中断

interrupt()方法可以用来请求终止线程。

当对一个线程调用interrupt方法时,线程的中断状态(boolean标志)会被置位。

判断当前线程是否中断,可使用Thread.currentThread.isInterrupt()

中断并不是强制终止线程,中断线程只是引起当前线程的注意,由它自己决定是否响应中断。【有些(非常重要的)线程会处理完异常后继续执行,并不理会中断;但是更加普遍的情况是:线程简单的将中断作为一个终止请求。】

线程销毁

线程销毁的几种情景:

线程结束,自行关闭

异常退出

通过interrupt()修改isInterrupt()标志进行结束

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

Thread t= newThread(){

@Overridepublic voidrun() {

System.out.println("I will start work.");while(!isInterrupted()){

System.out.println("working....");

}

System.out.println("I will exit.");

}

};

t.start();

TimeUnit.MICROSECONDS.sleep(100);

System.out.println("System will exit.");

t.interrupt(); }

使用volatile修饰的开关flag关闭线程(因为线程的interrupt标识很可能被擦除)【chapter04.FlagThreadExit】

public classFlagThreadExit {static class MyTask extendsThread{private volatile boolean closed = false;

@Overridepublic voidrun() {

System.out.println("I will start work.");while(!closed && !isInterrupted()){

System.out.println("working....");

}

System.out.println("I will exit.");

}public voidclosed(){this.closed = true;this.interrupt();

}

}public static void main(String[] args) throwsInterruptedException {

MyTask task= newMyTask();

task.start();

TimeUnit.MICROSECONDS.sleep(100);

System.out.println("System will exit.");

task.closed(); }

}

多线程API

方法

返回值

作用

yield()

static void

暂停当前正在执行的线程对象,并执行其他线程。

只有优先级大于等于该线程优先级的线程(包括该线程)才有机会被执行

释放CPU资源,不会放弃monitor锁

sleep()

static void

使当前线程休眠,其它任意线程都有执行的机会

释放CPU资源,不会放弃monitor锁

wait()

void

使当前线程等待

Object的方法

interrupt()

void

中断线程

可中断方法

interrupted()

static boolean

判断当前线程是否中断

isInterrupted()

boolean

测试线程是否已经中断

join()

void

在线程A内,join线程B,线程A会进入BLOCKED状态,直到线程B结束生命周期或者线程A的BLOCKED状态被另外的线程中断

可中断方法

可中断方法被打断后会收到中断异常InterruptedException.

yield()和sleep()的比较

都是Thread类的静态方法

都会使当前处于运行状态的线程放弃CPU

yield只会让位给相同或更高优先级的线程,sleep让位给所有的线程

当线程执行了sleep方法后,将转到阻塞状态,而执行了yield方法之后,则转到就绪状态;

sleep方法有可能抛出异常,而yield则没有【sleep是可中断方法,建议使用sleep】

sleep和wait的比较

wait和sleep方法都可以使线程进入阻塞状态

wait和sleep都是可中断方法

wait使Object的方法,sleep是Thread的方法

wait必须在同步代码中执行,而sleep不需要

在同步代码中,sleep不会释放monitor锁,而wait方法会释放monitor锁

sleep方法在短暂休眠后主动退出阻塞,而(没有指定时间的)wait方法则需要被其它线程notify或interrupt才会退出阻塞

wait使用

必须在同步当法中使用wait和notify方法(wait和notify的前提是必须持有同步方法的monitor的所有权)

同步代码的monitor必须与执行wait和notify方法的对象一致

public classWaitDemo {public static voidmain(String[] args) {

ExecutorService pool= Executors.newFixedThreadPool(2);

pool.execute(()->{synchronized (WaitDemo.class){

System.out.println("Enter Thread1...");

System.out.println(Thread.currentThread().getName()+" is waiting...");try{

WaitDemo.class.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

System.out.println("Thread1 is going...");

System.out.println("Shut down Thread1.");

}

});try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

pool.execute(()->{synchronized (WaitDemo.class) {

System.out.println("Enter Thread2...");

System.out.println(Thread.currentThread().getName()+" is notifying other thread...");

WaitDemo.class.notify();try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

System.out.println("Thread2 is going...");

System.out.println("Shut down Thread2.");

}

});

}

}

补充

弃用stop()和suspend()的原因

stop()用来终止一个线程,但是不安全的; stop()会终止当前线程的所有未结束的方法,包括run()。当前线程被终止,立即释放被它锁住的锁。这会导致对象处于不一致的状态。【在转账过程中,可能钱刚转出,还未转入另一个账户,线程就被中断。导致对象被破坏,数据出错,其他线程得到的将会是错误的对象数据】

suspend()用来阻塞一个线程,直至另一个线程调用resume()。suspend()会经常导致死锁。 调用suspend()的时候,目标线程会被挂起,但是仍然会持有之前获得的锁定。此时,其他线程都不能访问被锁定的资源。如果调用suspend()的线程试图获取同一个锁,那么线程死锁(被挂起的线程等着恢复,而将其挂起的线程等待锁资源)

suspend()的替代方案

应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

在实际开发中,调用start()启动线程的方法已不再推荐。应该从运行机制上减少需要并行运行的任务数量。如果有很多任务,要为每个任务创建一个独立线程的编程所付出的代价太大了。可以使用线程池来解决这个问题。

线程信息查看工具:JDK自带的Jconsole

Java task类需要自己销毁_并发编程之线程创建到销毁、常用API相关推荐

  1. 并发编程-05线程安全性之原子性【锁之synchronized】

    文章目录 线程安全性文章索引 脑图 概述 原子性synchronized 修饰的4种对象 修饰代码块 作用范围及作用对象 Demo 多线程下 同一对象的调用 多线程下不同对象的调用 修饰方法 作用范围 ...

  2. java 变量锁_并发编程高频面试题:可重入锁+线程池+内存模型等(含答案)

    对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一.因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系统.内存.CPU.编程语言等多方面的基础能力,更为考验一个程序 ...

  3. java实现分而治之_并发编程中一种经典的分而治之的思想!!

    写在前面 在JDK中,提供了这样一种功能:它能够将复杂的逻辑拆分成一个个简单的逻辑来并行执行,待每个并行执行的逻辑执行完成后,再将各个结果进行汇总,得出最终的结果数据.有点像Hadoop中的MapRe ...

  4. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  5. Java并发编程-ThreadPool线程池

    ThreadPool线程池 1.线程池的优势 1.1.引言 1.2.为什么要使用线程池 2.线程池的使用 2.1.架构说明 2.2.线程池的三大方法 2.2.1.newFixedThreadPool( ...

  6. Java并发编程:线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  7. 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )

    文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...

  8. Java并发编程一线程池简介

    推荐:Java并发编程汇总 Java并发编程一线程池简介 为什么我们需要使用线程池? 我们知道线程是一种比较昂贵的资源,我们通过程序每创建一个线程去执行,其实操作系统都会对应地创建一个线程去执行我们的 ...

  9. 【Java 并发编程】线程池机制 ( 线程池示例 | newCachedThreadPool | newFixedThreadPool | newSingleThreadExecutor )

    文章目录 前言 一.线程池示例 二.newCachedThreadPool 线程池示例 三.newFixedThreadPool 线程池示例 三.newSingleThreadExecutor 线程池 ...

  10. java并发编程与线程安全

    2019独角兽企业重金招聘Python工程师标准>>> 什么是线程安全 如果对象的状态变量(对象的实例域.静态域)具有可变性,那么当该对象被多个线程共享时就的考虑线程安全性的问题,否 ...

最新文章

  1. vector机器人 CAN I USE MULTIPLE VECTORS OR DEVICES? 我可以使用多个向量或设备吗?
  2. android8 Notification
  3. java之线程池面试题
  4. shiro表单登录认证及退出(自定义form认证器)
  5. 27.13. flock - manage locks from shell scripts
  6. 学习SAP项目成功实施的十大条件
  7. 石板切割问题c语言_天长井盖切割机切圆机市政窨井盖切圆机
  8. python sql 日期查询_Python--flask使用 SQLAlchemy查询数据库最近时间段或之前的数据...
  9. 前后端分离的思考与实践(二)
  10. 如何快速学好python语言_如何快速的学习Python语言
  11. android如何实现QQ信息通知,android NotificationListenerService监听通知栏(qq 微信 短信)...
  12. 信息学奥赛一本通 1022:整型与布尔型的转换 | OpenJudge NOI 1.2 09
  13. 《Effective Debugging:软件和系统调试的66个有效方法》——导读
  14. 很搞笑的一个病毒--还能提问 “死亡问答”(Sola)宅男病毒
  15. 米家扫地机器人充满电需要多长时间_【米家扫地机器人使用总结】充电|APP|清扫_摘要频道_什么值得买...
  16. tensorflow2.0自制神经网络数据集及预测结果(混淆矩阵)可视化
  17. 《指弹:Dream Catcher》
  18. 未来十年: 机器编程会取代程序员吗?
  19. 菜谱分享网站微信小程序开发说明(2)-数据库
  20. 著名企业求职面试指南

热门文章

  1. 【云原生】理解k8s中的Pod和容器设计模式
  2. 《美好企业》导读:企业家需要超越世俗的成功
  3. VSCode折叠所有区域代码快捷键
  4. vue 头像 上传 裁剪
  5. acme申请证书报错:Please update your account with an email address first.的修复方法
  6. 大脑中的CD19表达与CAR-T治疗关系
  7. 三国群英传服务器端架设修改,三国群英传OL单机架设视频教程
  8. 计算机 64虚拟内存设置方法,64位的WIN7,4G内存,虚拟内存怎么设置
  9. mysql就业方向_SQL数据库专业的就业方向
  10. matlab中min函数