java多线程并发(二)--线程的生命周期及方法详解

时间:2023-02-09 07:25:17

  上篇随笔介绍了线程的相关基础知识以及新启线程的几种方法,本片将继续介绍线程的生命周期及方法详解。

一、线程的生命周期

  在Thread代码中,线程的状态被分为6种

public enum State {
/**
* 尚未启动的线程处于此状态
*/
NEW, /**
* 在java虚拟机中执行或等待其他资源的线程处于此状态
*/
RUNNABLE, /**
* 被阻塞等待监视器锁定的线程处于此状态*/
BLOCKED, /**
* 正在等待另一个线程执行特定动作的线程处于此状态*/
WAITING, /**
* 正在等待另一个线程执行动作到达指定等待时间的线程处于此状态*/
TIMED_WAITING, /**
* 已退出的线程处于此状态
*/
TERMINATED;
}

  而通常我们讨论的状态为5种:

  1. 新建(New):创建尚未启动的线程;
  2. 就绪(Runnable):此时线程已经具备运行的能力,是“可执行状态”,但尚未运行,即调用了Thread类的start();
  3. 运行(Running):线程获得了CPU权限,由就绪状态进入运行状态(线程也只能由就绪状态进入运行状态);
  4. 阻塞(Blocked):阻塞状态是因为线程因为某种原因放弃CPU执行权,暂时停止运行。直到线程进入就绪状态,才有机会进入就绪状态,阻塞的情况分为3种:(1)等待阻塞--通过调用wait()方法,使线程等待某工作完成;(2)同步阻塞--线程获取synchronized同步锁失败,锁被其他线程占用;(3)其他阻塞--通过调用线程的sleep()或者join()发出IO请求时,线程会进入阻塞状态,当sleep状态超时或join等待线程终止、超时、或者IO处理完成是,线程重新进入就绪状态;
  5. 死亡(Dead):线程执行完了或者因为一场退出了run()方法,线程结束生命周期。

java多线程并发(二)--线程的生命周期及方法详解

 二、Thread常用方法

  1、常用构造方法:

Thread();
Thread(Runnable target) ;
Thread(Runnable target, String name);
Thread(String name)

  2、设置线程属性的方法:

public final void setDaemon(boolean on)//将此线程标记为daemon线程或用户线程
public final synchronized void setName(String name)//将此线程的名称更改为等于参数name 。
public void setContextClassLoader(ClassLoader cl)//设置此线程的上下文ClassLoader
public final void setPriority(int newPriority)//更改此线程的优先级

  3、获取线程属性的方法

public long getId()//返回此线程的标识符
public final String getName()//返回此线程的名称
public ClassLoader getContextClassLoader()//返回此Thread的上下文ClassLoader
public final int getPriority()//返回此线程的优先级
public Thread.State getState()//返回此线程的状态
public final ThreadGroup getThreadGroup()//返回此线程所属的线程组

  4、start()方法

public synchronized void start()

  start()方法启动线程,开始调用线程的run()方法。

  5、run()方法

public void run()

  run()是线程在执行状态下需要执行的方法,线程调用start()之后会调用该方法。不能直接使用该方法,直接使用的话,并不是通过线程调用,而是仅仅作为一个普通对象的普通方法调用。请看如下例子:

class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
System.out.println("当前是[" + Thread.currentThread().getName() + "]线程!");
}
} public class Test{
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new MyThread("线程1");
Thread thread2 = new MyThread("线程2"); thread1.start();
Thread.sleep(500);
thread2.run(); }
}

  结果如下:

java多线程并发(二)--线程的生命周期及方法详解

  可以看到直接调用thread2.run(),新建的线程对象并没有启动,当前线程仍然是main线程。

  7、currentThread()

public static native Thread currentThread;//返回对当前正在执行的线程对象的引用

  currentThread()方法是静态本地方法,不是通过线程对象调用,而是通过Thread类调用。

  6、sleep()方法

public static native void sleep(long millis) throws InterruptedException;

  7、sleep()方法也是静态本地方法,通过thread调用。 

class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
System.out.println("当前是[" + Thread.currentThread().getName() + "]线程!");
long time1 = System.currentTimeMillis();
try {
Thread.sleep(500);
long time2 = System.currentTimeMillis();
System.out.println("[" + Thread.currentThread().getName() + "]线程休眠" + (time2-time1) + "毫秒!");
} catch (InterruptedException e) {
e.printStackTrace();
} }
} public class Test{
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<3;i++){
Thread thread = new MyThread("线程" +i);
thread.start();
}
}
}

  执行结果:

java多线程并发(二)--线程的生命周期及方法详解

  8、isAlive()方法

public final native boolean isAlive();//测试这个线程是否活着,线程已经被启动,并且尚未死亡

  9、interrupt()、isInterrupted()和interrupted()

public void interrupt() //中断这个线程(线程实例)

/**
* 测试当前线程是否中断。 该方法可以清除线程的中断状态 。
* 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false
* (除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经
* 检查过)。
* 忽略线程中断,因为线程在中断时不存在将被该方法返回false所反映
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
} /**
* 测试这个线程是否被中断。 线程的中断状态不受此方法的影响。(线程实例)
*/
private native boolean isInterrupted(boolean ClearInterrupted)

  interrupt()方法用来中断请求,调用它时,并不是直接将线程中断,而是将线程的中断状态位改为中断状态,isInterrupted()返回为true;interrupt()的作用更像是通知线程中断,线程选择执行完任务后再中断。

  下面两个例子更好的解释这种原理:

  • class MyThread extends Thread{
    @Override
    public void run() {
    for (int i=0;i<1000;i++){
    System.out.println("是否已下达中断指令?" + isInterrupted());
    }
    }
    } public class Test{
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new MyThread();
    thread.start();
    Thread.sleep(10);
    thread.interrupt();
    }
    }

  执行结果: java多线程并发(二)--线程的生命周期及方法详解

  可以看到,在调用interrupt()方法后,线程的run()方法仍在执行,直到正常执行结束。

  • class MyThread extends Thread{
    @Override
    public void run() {
    for (int i=0;i<1000;i++){
    System.out.println("是否已下达中断指令?" + interrupted());
    }
    }
    } public class Test{
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new MyThread();
    thread.start();
    Thread.sleep(10);
    thread.interrupt();
    }
    }

    执行结果:java多线程并发(二)--线程的生命周期及方法详解

    可以看到,再调用interrupt()方法后,中断标志位已经修改为true,interrupted()的第一次调用返回true,后面调用均返回false。这是因为,当中断标志位被interrupt()修改位true时,在调用interrupted(),确实会返回true,但这时本地方法interrupted()会将中断标志位恢复,所以后面调用均显示false。

  同时需要注意interrupted()返回的是当前线程的标志位!!!!例子:

class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i="+(i+1));
}
}
} public class Test{
public static void main(String[] args) throws InterruptedException {
MyThread thread=new MyThread();
thread.start();
thread.interrupt();
System.out.println("第一次调用thread.isInterrupted():"+thread.isInterrupted());
System.out.println("第二次调用thread.isInterrupted():"+thread.isInterrupted());
//测试interrupted()函数
System.out.println("第一次调用thread.interrupted():"+thread.interrupted());
System.out.println("第二次调用thread.interrupted():"+thread.interrupted());
System.out.println("thread是否存活:"+thread.isAlive());
}
}

  上一个例子中,两次interrupted()方法都返回false,是因为该方法在main方法中调用,因此反应的并不是MyThread实例对象是否中断,而是main线程是否中断!!!!

  interrupt()并不是真正意义上的中断线程,而是通知线程该中断了。

  对于一个线程,调用interrupt()时:

  ①正常执行任务时,当线程处于阻塞状态(例如处于sleep, wait, join 等状态),线程会立即退出阻塞状态,并抛出一个InterruptedException异常。仅此而已。

   ②当线程处于正常执行状态,则将该线程的中断标志改为true,并不影响线程的正常运行。

  stop()虽然可以是线程终止,但是并不能一定及时释放资源,已废弃。

  当确实需要即刻中断线程时,可以这样做:

  ①在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。(通过while关键字)

  ②在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程,return关键字结束线程。)

  10、yield()方法

public static native void yield();//暂停当前正在执行的线程对象,并执行其他线程使用较少

  ①yield()使当前线程交出CPU权限,是CPU去执行其他线程;

  ②yield()方法和sleep()方法类似,不会释放锁,但yield()方法不能控制具体交出CPU的时间;

  ③yield()只能使相同优先级的线程获取CPU执行的机会;

  ④yield()不会是线程进入阻塞状态,只会使线程重新进入就绪状态,等待获取CPU执行权的机会。

  11、join方法

public final void join() throws InterruptedException {
join(0);
} public final synchronized void join(long millis) throws InterruptedException;
public final synchronized void join(long millis, int nanos) throws InterruptedException;

  join()方法是等待调用方法的线程死亡,意思是该线程需要先执行完毕才会执行后面的线程。

  例子:

class MyThread extends Thread{

    public MyThread(String name){
setName(name);
}
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "]执行...");
}
} /**
*
*/ public class Test{
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new MyThread("线程1");
thread1.start();
thread1.join();
System.out.println("没有join()会优先打印这条语句!!!");
}
}

  执行结果:

java多线程并发(二)--线程的生命周期及方法详解

  12、stop()、resume()、suspend()均已不建议使用,就不介绍了。

java多线程并发(二)--线程的生命周期及方法详解的更多相关文章

  1. Java多线程并发02——线程的生命周期与常用方法,你都掌握了吗

    在上一章,为大家介绍了线程的一些基础知识,线程的创建与终止.本期将为各位带来线程的生命周期与常用方法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程生命周期 一个线程不是被创建了 ...

  2. &OpenCurlyDoubleQuote;全栈2019”Java多线程第八章:放弃执行权yield&lpar;&rpar;方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. Java多线程学习&lpar;三&rpar;---线程的生命周期

    线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...

  4. &OpenCurlyDoubleQuote;全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. &OpenCurlyDoubleQuote;全栈2019”Java多线程第二十三章:活锁(Livelock)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. &OpenCurlyDoubleQuote;全栈2019”Java多线程第十六章:同步synchronized关键字详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. Android四大组件之——Activity的生命周期(图文详解)

        转载请在文章开头处注明本博客网址:http://www.cnblogs.com/JohnTsai       联系方式:JohnTsai.Work@gmail.com       [Andro ...

  8. Java构造和解析Json数据的两种方法详解二

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Jso ...

  9. Java构造和解析Json数据的两种方法详解二——org&period;json

    转自:http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/24/3096437.html 在www.json.org上公布了很多JAVA下的jso ...

随机推荐

  1. 关于上传app遇到的一些棘手的小问题

    最近上传了一个app,由于是第一次上传,花费在这上面的时间不比做app的耗时短啊....说多了都是泪.下面为了不让大家掉坑里去,分享一些小经验: 1.使用别人的开发者账号上传完自己的app(本人暂无开 ...

  2. grunt配置太复杂?使用Qbuild进行文件合并、压缩、格式化等处理

    上次简单介绍了下Qbuild的特点和配置,其实实现一个自动化工具并不复杂,往简单里说,无非就是筛选文件和处理文件.但Qbuild的源码也并不少,还是做了不少工作的. 1. 引入了插件机制.在Qbuil ...

  3. 关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造)

    关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造) 摘要:在中文搜索中的标点.符号往往也是有语义的,比如我们要搜索“C++”或是“C#”,我们不希望搜索出来的全是“C”吧?那样对程 ...

  4. 【转】Android Listener侦听的N种写法

    原文网址:http://blog.csdn.net/ithomer/article/details/7489274 Android中,View的Listener方法,在是否使用匿名类匿名对象时,有各种 ...

  5. IOS开发-UI学习-UISlider(滑动条)的使用

    滑动条即UISlider,是我们常见的软件中设置音量,亮度等的滑条,初始化及基本设置如下: // 新建滑动条 UISlider *slider = [[UISlider alloc]initWithF ...

  6. MySQL数据库 —子查询&comma;联合查询

    一 使用IN关键字的子查询 1.查询游戏类型是'棋牌类' 的游戏的分数信息 游戏分数表中并未包含游戏类型信息 思路一:采用链接查询 思路二: 分两步进行,首先找到所以'棋牌类'游戏的编号,再以这一组编 ...

  7. python3基础视频教程

    随着目前Python行业的薪资水平越来越高,很多人想加入该行业拿高薪.有没有想通过视频教程入门的同学们?这份Python教程全集等你来学习啦! python3基础视频教程:http://pan.bai ...

  8. 虚拟机安装Linux系统

    Mware Workstation 12 序列号: 5A02H-AU243-TZJ49-GTC7K-3C61N 步骤一: 右键-->新建虚拟机 步骤二:自定义(高级)-->下一步 步骤三: ...

  9. 20165318 2017-2018-2 《Java程序设计》第二周学习总结

    20165318 2017-2018-2 <Java程序设计>第二周学习总结 教材学习内容总结 本周学习了第二章和第三章的内容,掌握了Java中基本数据类型.数组.运算符.表达式和语句等方 ...

  10. Python开发基础-Day7-闭包函数和装饰器基础

    补充:全局变量声明及局部变量引用 python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量 global关键字用来在函数或其 ...