1.1 认识线程

  线程是轻量级进程,也是程序执行的一个路径,每一个线程都有自己的局部变量表、程序计数器(指向正在执行的指令指针)以及各自的生命周期,现代操作系统中一般不止一个线程在运行。比如说,当我们启动了一个JVM的时候,操作系统创建一个新的进程(JVM进程),JVM进程中将会创建很多线程。总而言之,线程是一个时间段的描述,是CPU工作时间段的描述。你可以想象成一个生命体,从生到死。

1.2 线程的生命周期

  上文说过我们可以抽象的理解一个线程是一个CPU工作时间段的生命体,那么他的生命周期是如何的呢?请看图1-1。

  由图可知,线程的生命周期大致分为以下5个主要阶段

  • NEW
  • RUNNABLE
  • RUNNING
  • BLOCKED
  • TERMINATED

1.2.1 线程的NEW状态

  用java关键字new创建一个Thread对象的时候,该线程的状态为NEW状态,准确的说,和你用关键字new创建一个普通的java对象没有什么区别,NEW状态通过start()方法进入RUNNABLE状态。有些软文上说创建一个线程的方式有2种,继承Thread,实现Runnable接口。这种说法是不准确的,也可以说是错误的。在JDK中代表线程的只有Thread这个类,所以准确的说创建线程只有一种方式,那就是构造Thread类。而实现线程要执行的业务逻辑是重写Thread的run()或者实现Runnable接口的run()。最终将Runnable实例用作构造Thread的参数。无论是哪一种,都是想将线程的控制本身和业务逻辑的运行分离开来。

1.2.2 线程的RUNNABLE状态

  线程对象调用start()方法,才真正的在JVM进程中创建了一个线程,线程运行与否和进程一样听令与CPU调度,所以这里只是成为RUNNABLE状态,具备执行的资格,但是并没有真正的执行起来而是在等待CPU调度。

  严格来说,RUNNABLE的线程只能意外终止或者进入RUNNING状态。

1.2.3 线程的RUNNING状态

  一旦CPU通过轮询选中了线程,那么它才真正开始执行自己的逻辑代码,正在RUNNING状态的线程也是RUNNABLE的,但是反之不成立。

  该状态可能发生如下转换

  • 直接进入TERMINATED状态,比如调用stop()方法(JDK已经不推荐使用)
  • 进入BLOCKED状态,比如调用了sleep()或者wait()方法。
  • 进入某个阻塞的IO操作
  • 获取某个锁资源
  • CPU调度使该线程放弃执行,进入RUNNABLE状态
  • 线程主动调用yield()方法,放弃执行权, 进入RUNNABLE状态

1.2.4 线程的BLOCKED状态

  该状态可能切换至以下状态

  • 直接进入TERMINATED,比如调用stop()方法(JDK已经不推荐使用)
  • 线程阻塞操作完成,进入RUNNABLE状态,比如读取到了数据字节
  • 线程完成了指定时间的休眠,进入RUNNABLE状态
  • wait中的线程被其他线程notify/notifyAll唤醒,进入RUNNABLE状态
  • 线程获取到了锁资源,进入RUNNABLE状态
  • 线程在阻塞过程中被打断,其他线程调用了interrupt()方法,进入RUNNABLE状态

1.2.5 线程的TERMINATED状态

  TERMINAED状态是一个最终状态,相当于一个生命体的死亡,不会切换到其他状态了,意味着整个生命周期的结束。下面这些情况将会导致线程进入TERMINATED状态。

  • 线程运行正常结束
  • 线程运行出错意外结束
  • JVM crash,导致这个进程里的所有线程都结束。

1.3 Thread API介绍

1.3.1 sleep()方法介绍

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

public static void sleep(long millis, int nanos) throws InterruptedException;

  sleep()方法会使当前线程进入指定毫秒数的休眠,休眠有一个非常重要的特性,它不会放弃monitor锁的所有权。可以这么记,抱着锁(你)睡觉。

1.3.2 yield()方法介绍

public static native void yield();

  yield()会提醒调度器我愿意放弃当前的CPU资源,如果CPU的资源不紧张,则会忽略这种提示。

  调用yield()方法会使当前线程从RUNNING状态切换到RUNNABLE状态。yield()只是一个提示,CPU调度器并不会担保每次都能满足yield提示。

1.3.3 interrupt()方法介绍

  线程interrupt()方法是一个非常重要的API,有些方法(wait(),sleep(),join(),io操作等方法)的调用会使得当前线程进入阻塞状态,而调用当前线程的interrupt方法,就可以打断阻塞。因此这些方法有时会被称为可中断方法,打断一个线程并不等于该线程的生命周期结束,仅仅是打断了当前线程的阻塞状态。接下来我们看一个例子。

/**
* 打断阻塞状态测试类
*
* @author GrimMjx
*/
public class InterruptTest { public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
System.out.println("I am interrupted.");
}
}); thread1.start(); TimeUnit.SECONDS.sleep(2);
thread1.interrupt();
}
}

  上面的代码先创建出一个线程,休眠1分钟,但是main线程在休眠2秒钟之后调用interrupt()方法打断thread1。

  interrupt()到底做了什么事情呢?在一个线程内部存在一个名为interrupt flag的标识,如果一个线程被interrupt,那么它的flag会被设置,但是如果当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除。

1.3.4 join()方法介绍

public final void join() throws InterruptedException;

  join某个线程A,会使当前线程进入等待状态,直到线程A结束生命周期,或者到达给定的时间,那么在此期间,当前线程都是出于BLOCKED状态的,而不是线程A。接下来我们看一个例子。

/**
* Join方法测试类
*
* @author GrimMjx
*/
public class JoinTest { public static void main(String[] args) throws InterruptedException {
Thread thread = create("thread1");
thread.start(); //执行join方法
thread.join(); //主线程开始工作
System.out.println(Thread.currentThread().getName() + " is processing");
TimeUnit.SECONDS.sleep(1);
} private static Thread create(String name) {
return new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " is processing");
}, name);
}
}

  join()会使当前线程永远等待下去,直到期间被另外的线程中断,或者join的线程执行结束。concurrent包里的CountDownLatch和CyclicBarrier都可以实现和join()方法一样的功能,当某些线程达到一个点做一件事情。这个有兴趣的同学可以自己去学习使用。并掌握其中的不同点和共同点。

Java并发专题(一)认识线程的更多相关文章

  1. JAVA技术专题综述之线程篇(1)

    本文详细介绍JAVA技术专题综述之线程篇 编写具有多线程能力的程序经常会用到的方法有: run(),start(),wait(),notify(),notifyAll(),sleep(),yield( ...

  2. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  3. 【Java并发专题之二】Java线程基础

    使用线程更好的提高资源利用率,但也会带来上下文切换的消耗,频繁的内核态和用户态的切换消耗,如果代码设计不好,可能弊大于利. 一.线程 进程是分配资源的最小单位,线程是程序执行的最小单位:线程是依附于进 ...

  4. Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等

    继续并发专题~ FutureTask 有点类似Runnable,都可以通过Thread来启动,不过FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞. 由于:Fu ...

  5. Java 并发专题 : CyclicBarrier 打造一个安全的门禁系统

    继续并发专题~ 这次介绍CyclicBarrier:看一眼API的注释: /** * A synchronization aid that allows a set of threads to all ...

  6. 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition

    img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...

  7. 【java并发编程实战】-----线程基本概念

    学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...

  8. Java并发编程:进程和线程的由来(转)

    Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...

  9. Java 并发专题 : Semaphore 实现 互斥 与 连接池

    继续并发方面的知识.今天介绍Semaphore,同样在java.util.concurrent包下. 本来准备通过例子,从自己实现到最后使用并发工具实现,但是貌似效果并不是很好,有点太啰嗦的感觉,所有 ...

  10. Java 并发专题 :闭锁 CountDownLatch 之一家人一起吃个饭

    最近一直整并发这块东西,顺便写点Java并发的例子,给大家做个分享,也强化下自己记忆. 每天起早贪黑的上班,父母每天也要上班,话说今天定了个饭店,一家人一起吃个饭,通知大家下班去饭店集合.假设:3个人 ...

随机推荐

  1. xampp访问phpmyadmin访问不了

    我的xampp版本是xampp-linux-x64-5.6.15-2-installer.run, 浏览器输入“我的ip/phpmyadmin”出现如下问题: Access forbidden! Ne ...

  2. H5本地存储详细使用教程(localStorage + JSON数据存储应用框架)

    一.Web Storage教程 1.概述: 对于Web Storage来说,实际上是Cookies存储的进化版.如果了解Cookie的人几乎一看Web Storage就会用,如果你从来没用过没了解过C ...

  3. 开发中少不了的Fun -- 微信开发IOS端alert/confirm提示信息,去除网址(URL)的方法

    在微信公众号开发的时候在使用[alert/confirm]弹出提示或者警告信息的时候,[alert/confirm]会将该公众号的网址显示出来,这样很不美观.所以很多时候我们会选择去除那个网址提示内容 ...

  4. window7环境下ZooKeeper的安装及运行

    简介 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...

  5. VUE 出现Access to XMLHttpRequest at 'http://192.168.88.228/login/Login?phone=19939306484&password=111' from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 'Access-Contr

    报错如上图!!!!    解决办法首先打开 config -> index.js ,粘贴 如下图代码,'https://www.baidu.com'换成要访问的的api域名,注意只要域名就够了, ...

  6. unittest测试套件

    测试套件就是测试集,测试集是测试用例的集合. a.按用例顺序执行(addtest) 当addtest与unittest的测试规则冲突时,仍然按照ASCII码的顺序执行. import unittest ...

  7. 关于 Senparc.Weixin.Cache.Redis 引用的 StackExchange.Redis 版本不匹配的反馈测试

    推测原因是老系统中有地方引用了旧版本的 StackExchange.Redis,原因是 StackExchange.Redis 1.2.6 版本未提供针对 .net 4.6 以上的支持,导致库引用会失 ...

  8. Java中,多态的实现有哪些要求?实现多态的关键技术?

     多态指的是允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).实现多态的方法是动态绑定( Dynamic Binding),动态绑定 ...

  9. RabbitQM使用笔记

    一:安装  and  卸载 curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.r ...

  10. 【mysql】Date和String的互相转换(DATE_FORMAT & STR_TO_DATE)

    1.Date  ——>  String 使用的函数:DATE_FORMAT(date,format)     date:需要转换的日期       format:格式化的样式 format样式整 ...