前言
本篇博客主要从三个方面记述。第一,对线程进行简要的介绍,理解什么是线程,为什么使用多线程。第二,介绍线程拥有的状态,以及线程各个状态之间的转换,并举相关的例子进行说明。第三,介绍线程的操作,对线程的创建、终止、中断、挂起和继续执行等进行举例说明。
线程的介绍
讲到线程,首先会提到的是进程,具体什么是线程,什么是进程尼?
概念上看,进程是具有一定独立功能的程序,它是关于某个数据集合的一次运行活动,进程是系>统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位。>线程是轻量级的进程,是程序执行的最小单元。
我们知道程序的并发设计,带来了生产的高效性。当然前提是正确的使用好程序的并发性。那么为什么需要引入线程尼?
一方面,在进程内创建、终止线程比创建、终止进程要快;
另一方面,同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换。
线程的状态
Java线程的生命周期中可能会处于6个状态,JDK中的State枚举出了以下六个状态。
public enum State {
NEW,//初始状态,线程被构建,但还没被调用start方法
RUNNABLE,//运行状态,Java线程中将操作系统中的就绪和运行两种状态统称为“运行中”
BLOCKED,//阻塞状态,表示该线程阻塞于锁
WAITING,//等待状态,表示该线程需要等待其他线程做出一些特定的通知
TIMED_WAITING,//超时等待状态,可以设置阻塞等待的时间,超过时间自动返回。
TERMINATED;//终止状态,表示该线程运行完毕
}
其中RUNABLE状态和操作线程叙述的运行状态是有区别的,RUNABLE状态笼统的将“就绪和运行”归为运行中状态
状态图说明:
1.NEW,刚创建的线程,还没开始执行,也就是说线程中的代码还没开始运行。
2.RUNNABLE,运行中状态。
2.1当线程对象调用start方法,线程进入就绪状态,此时还是没有开始执行线程代码,只是该线程进入可运行线程池,等待被调度线程选中,获取CPU运行时间片。
2.2如果该线程进入就绪状态后,获取到CPU运行时间片,进入运行状态,调度该线程并运行run方法里面的代码,即线程中的代码开始执行。
3.BLOCKED,阻塞状态,线程执行过程中遇到了synchonrized同步块,将会进入阻塞状态,此时,该线程便会停止运行,直到获取到该同步块的锁。此时的并发级别是阻塞。
4.WAITING和TIMED_WAITING,等待和超时等待状态,两个状态都表示等待,但是区别在于WAITING是无时间限制的等待,而TIMED_WAITING是有时间限制的等待。一旦进入到等待状态,该线程的代码也就会停止继续运行,直到其他线程进行通知唤醒,该线程才会回到运行状态。TIMED_WAITING在等待到超过一定的时间后也会返回到运行状态。通常搭配使用的是wait()和notify()方法。
5.TERMINATED,结束状态,即线程执行完毕,或者线程运行完run方法后将会进入到终止状态,当然也可能发生其他因异常而进入终止状态的情况。
线程的操作
线程的创建
Java多线程开发中提供了3中创建线程的方式。
方式一 继承Thread类实现线程类
//1.继承Thread
public class Demo1Thread extends Thread{
//2.复写run方法,线程执行的代码体
@Override
public void run() {
//something to do
......
}
}
//3.启动线程,进入就绪状态
new Demo1Thread().start()
方式二 实现Runnable接口实现线程类
//1.实现Runnable接口
public class Demo2Thread implements Runnable{
//2.复写run方法,线程执行的代码体
@Override
public void run() {
//something to do
......
}
}
//启动线程
new Thread(new Demo2Thread()).start()
方式三 通过Callable和Future来创建线程
//1.实现Callable接口,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
class Demo3Thread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//something to do
......
//返回线程的运行结果
return new Integer(9);
}
}
//2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
Demo3Thread t3 = new Demo3Thread();
FutureTask<Integer> ft = new FutureTask<>(t3);
//3.启动线程
new Thread(ft).start();
//4.获取线程运行结果
ft.get();
线程的中断、挂起和继续执行
中断,它是线程的一个标识位属性,表示一个运行的线程是否被其他线程进行了中断操作。可以比拟为其他线程对自正在运行的该线程打了个需要中断的招呼,是否需要停止,还得看运行的线程怎么做决定。
注意:当一个线程处于中断状态时,如果再由wait、sleep以及jion三个方法引起的阻塞,那么JVM会将线程的中断标志重新设置为false
public class Demo {
public static void main(String[] args) {
try {
//1 此处是当前运行的主线程main
Demo2Thread thread1 = new Demo2Thread();
thread.start();
Thread.sleep(2000);
//1.1 当前运行的主线程main向运行的thread1线程发送中断信号(打招呼)
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
//2.结束中断信号的线程Demo2Thread
public class Demo2Thread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
//2.1判断是否接收到中断信号,即自身的中断标识是否被置为true
if (this.interrupted()) {
System.out.println("should be stopped and exit");
break;
}
System.out.println("i=" + (i + 1));
}
//2.2尽管线程被中断,但并没有结束运行。这行代码还是会被执行
System.out.println("this line is also executed. thread does not stopped");
}
}
//3.获取中断信号的区别。
//3.1 interrupted函数是作用于当前线程,isInterrupted 函数是作用于调用该方法的线程对象所对应的线程。
//3.2 只有当前线程才能清除自己的中断位即interrupted函数。
线程的挂起,简单的说就是让线程进入“非可执行”状态下,在这个状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行。在线程挂起后,可以通过重新唤醒线程来使之恢复运行。Java中通常使用suspend()和resume()和stop()来完成线程的暂停、恢复和停止。但现在已不建议使用。
不建议的原因:
suspend()挂起后,线程并不会释放已经占有的资源(锁),是属于占着资源进入睡眠状态,这容易引起需要它占有的资源其他线程处于等待或阻塞状态。
stop()方法终结一个线程后不会保证线程的资源正确的得到释放。
线程的终止
线程的终止,即线程的结束状态,不建议使用stop强行终止线程,原因如上,可以通过发送中断信号,或者设置线程的boolean值来控制线程任务是否终止。
public class Demo {
public static void main(String[] args) {
try {
//1 此处是当前运行的主线程main
Demo2Thread thread1 = new Demo2Thread();
thread.start();
Thread.sleep(2000);
//1.1 当前运行的主线程main向运行的thread1线程发送中断信号(打招呼)
thread.interrupt();
//1.2主线程安全的停止thread线程
//thread.setStop();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
//2.需要终止的线程Demo2Thread
public class Demo2Thread extends Thread {
private volatile boolean on =true;
@Override
public void run() {
//2.1该线程是否终止或者饥饿到中断
while(on&&!this.interrupted())
{
//do something
......
}
//2.2跳出循环
}
public void setstop(){
on=false;
}
}