本章主要介绍线程的概念,以及线程在Java中的主要作用,并且详细讲解了线程的生命周期,以及生命周期每个状态之间的切换方法。
线程介绍
对计算机来说,每一个任务都是一个进程,每个进程内存至少有一个线程(Thread)是在运行中,有时线程也成为轻量级的进程。
线程是程序执行的一个路径,每一个线程都有自己的局部变量表、程序计数器(指向正在执行的指令指针)以及各自的生命周期,现代操作系统中一般都不止一个线程在运行,当启动了一个Java虚拟机(JVM)时,从操作系统开始就会创建一个新的进程(JVM进程),JVM进程中将会派生或者创建很多线程。
代码样式
/**
* 创建一个多线程实例
*/
public class TryConcurrency {
public static void main(String[] args) {
// 通过匿名内部类的方式创建线程,并重写run方法
// new Thread(){
//
// @Override
// public void run(){
// browseNews();
//
// }
//
// }.start();
//
// enjoyMusic();
// Java8 Lambda改造上面的代码
new Thread(TryConcurrency::enjoyMusic).start();
browseNews();
}
private static void browseNews() {
for (;;){
System.out.println("Uh-huh,the good news .");
sleep(1);
}
}
private static void enjoyMusic() {
for (;;){
System.out.println("Uh-huh,enjoy music .");
sleep(1);
}
}
private static void sleep(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行截图:
线程的声明周期讲解
接下来,依次进行讲解
1.线程的NEW状态
用关键new创建一个Thread对象时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态。
它只是Thread对象的状态,在没有start之前,该线程根本不存在。
NEW状态通过start方法进入RUNNABLE状态
2.线程的RUNNABLE状态
线程对象进入RUNNABLE状态必须调用start方法,此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不一定立即执行。要听令于CPU调度。 我们把这个中间状态成为可执行状态(RUNNABLE),具备执行的资格。并没有真正执行而是等待CPU调度
只能进入RUNNING状态或者意外终止
3.线程的RUNNING状态
CPU通过轮询或者其他方式从任务可执行队列中选中了线程,才真正执行自己的逻辑代码
一个正在RUNNING状态的线程事实上也是RUNNABLE的,反过来则不成立。
可以进入的状态(三种):
TERMINATED: 调用JDK已经不推荐使用的stop方法或者判断某个逻辑标识
BLOCKED : 调用sleep或wait方法加入waitSet中
进行某个阻塞的IO操作
获取某个锁资源
RUNNABLE: 由于CPU的调度器轮询使该线程放弃执行
主动调用yield方法,放弃CPU执行权
线程的BLOCKED状态
进入该状态原因(三种):
1.调用sleep或wait方法加入waitSet中
2.进行某个阻塞的IO操作
3.获取某个锁资源
可以进入的状态 :
TERMINATED : 调用JDK已经不推荐使用的stop方法或意外死亡(JVM Crash)
RUNNABLE : 线程阻塞的操作结束,比如读取到了想要的数据字节
完成指定时间的休眠
Wait中的线程被其他线程notify/notifyall唤醒
获取到了某个锁资源
线程阻塞过程中被打断,调用了interrupt方法
线程的TERMINATED状态
TERMINATED是一个线程的最终状态,不会切换到其他任何状态,代表线程的整个声明周期都结束了
进入该状态原因(三种):
1.线程运行正常结束,结束生命周期
2.线程运行出错意外结束
3.JVM Crash, 导致所有的线程都结束
线程的start方法剖析:模板设计模式在Thread中的应用
Thread类中的run和start是一个比较经典的模板设计模式,父类编写算法结构代码,子类实现具体逻辑
start方法
分析Thread的start方法,在调用了start方法之后到底进行了什么操作。
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
在这里,最核心的就是start0 本地方法,也是JNI方法
private native void start0();
当开始执行这个线程的时候,JVM会调用该线程的run方法。换而言之。 run方法就是被JNI的start0方法调用的
1.Thread被构造的后的NEW状态,threadStatus这个内部状态为0
2.不能两次启动Thread,只有第一个线程在运行。第二个线程失败 Exception in thread "main" java.lang.IllegalThreadStateException
3.线程启动,会加入ThreadGroup中
4.TERMINATED无法再调用start方法,无法回归到其他状态。
run方法
线程的执行单元
如果,没有使用runnable接口对其进行构造,那么run方法本事就是一个空的实现:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
实例:模拟营业大厅叫号程序 (存在线程安全问题,后续会讲解)
四个线程工作,每天最多出50个号码
TicketWindow代表出号机
public class TicketWindow extends Thread {
public static void main(String[] args) {
TicketWindow t1 = new TicketWindow("一") ;
t1.start();
TicketWindow t2 = new TicketWindow("二") ;
t2.start();
TicketWindow t3 = new TicketWindow("三") ;
t3.start();
TicketWindow t4 = new TicketWindow("四") ;
t4.start();
}
private final String name ;
private static final int MAX = 50 ;
private static int index = 1 ;
public TicketWindow(String name){
this.name = name ;
}
@Override
public void run(){
while (index <= MAX ) {
System.out.println("柜台:"+name +" 当前叫号" + ( index++ ) );
}
}
}
Runnable接口的引入以及策略模式在Thread中的使用
/**
* The <code>Runnable</code> interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called <code>run</code>.
* <p>
* This interface is designed to provide a common protocol for objects that
* wish to execute code while they are active. For example,
* <code>Runnable</code> is implemented by class <code>Thread</code>.
* Being active simply means that a thread has been started and has not
* yet been stopped.
* <p>
* In addition, <code>Runnable</code> provides the means for a class to be
* active while not subclassing <code>Thread</code>. A class that implements
* <code>Runnable</code> can run without subclassing <code>Thread</code>
* by instantiating a <code>Thread</code> instance and passing itself in
* as the target. In most cases, the <code>Runnable</code> interface should
* be used if you are only planning to override the <code>run()</code>
* method and no other <code>Thread</code> methods.
* This is important because classes should not be subclassed
* unless the programmer intends on modifying or enhancing the fundamental
* behavior of the class.
*
* @author Arthur van Hoff
* @see java.lang.Thread
* @see java.util.concurrent.Callable
* @since JDK1.0
*/
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
只定义了一个无参数,无返回值的run方法
策略模式在Thread中的应用
本文来源于:
《Java高并发编程详解:多线程与架构设计》 --汪文君