目录
什么是线程(百度百科)
操作系统(OS)线程状态
java创建线程的方法
JVM线程状态转换
线程数据结构
备注
什么是线程(百度百科)
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
操作系统(OS)线程状态
OS线程的状态是以CPU资源为核心定义(忽略硬盘、网卡等资源):
- 开始状态(new):产生一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源,因此只能启动或终止它
- 就绪状态(ready):start()方法产生运行线程所必须的资源,调度线程执行,此时并未真正执行,需要和其他线程竞争cpu资源
- 运行状态(running):start()方法之后线程竞争到了cup资源进入运行状态
- 等待状态(waiting):线程让出cup使用权,暂时停止运行的状态
(1)等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,进入这个状态后, 是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒, (2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用。 (3)其他阻塞:运行的线程执行sleep(…)或join(…)方法,或者发出了I/O请求时会把该线程置为阻塞态, 当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 结束状态(terminated):线程正常或异常结束进入死亡态
java创建线程的方法
- 方法:①继承Thread ②实现Runnable ③实现Callable接口
- 涉及对象:Thread、Runnable、RunnableFuture、Future、Callable、FutureTask
- 本质:以上3个创建线程方法的本质还是使用构造函数new Thread(…),只不过否传参Runnale不同罢了....
代码如下:new Thread(){}这种写法相当于是创建一个继承Thread的匿名内部类
- 实现Runnable方式:①线程任务和线程对象的解耦 ②多个线程对象可处理同一个任务,方便资源共享
- 实现Callable方式:与Runnable相比可以通过Future拿到返回结果或者异常
@Test
public void testThreadCreat() throws ExecutionException, InterruptedException {
log.info("我是主线程:{}", Thread.currentThread().getName());
//继承Thread: new Thread(){...}同创建匿名内部类
Thread thread1 = new Thread("yxf-继承Thread") {
@Override
public void run() {
log.info("我是子线程-:{}", Thread.currentThread().getName());
}
};
thread1.start();
//实现runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info("我是子线程-:{}", Thread.currentThread().getName());
}
};
Thread thread2 = new Thread(runnable, "yxf-实现Runnable");
thread2.start();
//实现Callable
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "我是子线程:" + Thread.currentThread().getName();
}
};
FutureTask<String> task = new FutureTask<String>(callable);
Thread thread3 = new Thread(task, "yxf-实现Callable");
thread3.start();
log.info(task.get());
}
代码执行结果
JVM线程状态转换
JVM的线程状态不同于OS的状态JVM的6种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
- OS是围绕cup定义,JVM围绕是其对象本身状态,对cup的ready->running这种时间分片轮转不关注
- JVM里的waiting更详细分为waiting、timed_waiting
- JVM无running状态,runnable 替代OS中的ready,running和部分waiting(OS中I/O阻塞为waiting状态,而JVM中则跳转到runnable状态)
/**
* 定义一个myThread继承Thread
*/
class myThread extends Thread{
public myThread(String name) {
super(name);
}
@Override
public void run(){
try {
//todo 业务逻辑
sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("运行中{},的状态:{}", this.getName(), this.getState());
}
}
首先我们测试下正常的状态流程:new-runnable-terminated
/**
* 线程状态转换测试
* new-runable-terminated
*/
@Test
public void testState1() throws InterruptedException {
//new:初始化线程后为开始状态,此时线程不会获得cpu资源
myThread thread = new myThread("线程...A");
log.info("{},的状态:{}", thread.getName(), thread.getState());
//RUNNABLE:准备运行(有可能还未获得cpu资源)
thread.start();
log.info("{},的状态:{}", thread.getName(), thread.getState());
Thread.sleep(10000L);
// TERMINATED 线程远行结束后状态
log.info("{},的状态:{}", thread.getName(), thread.getState());
}
runnable->blocked
/**
* 线程状态转换测试
* runable-blocked
*/
@Test
public void testState2() throws InterruptedException {
//定义锁对象
Object lock = new Object();
//定义Runnable类型的匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (lock) { //synchronized
//todo 业务代码
log.info("运行中{},的状态:{}", Thread.currentThread().getName(),
Thread.currentThread().getState());
long time = System.currentTimeMillis();
//距离time之后的1s内运行
while(System.currentTimeMillis()-time<2000L){
}
}
}
};
//new:初始化线程后为开始状态
Thread thread_a = new Thread(runnable, "线程...a");
Thread thread_b = new Thread(runnable, "线程...b");
log.info("new之后{},的状态:{}", thread_a.getName(), thread_a.getState());
log.info("new之后{},的状态:{}", thread_b.getName(), thread_b.getState());
//启动线程
thread_a.start();
thread_b.start();
//沉睡1s后查看线程状态
Thread.sleep(1000L);
log.info("start之后{},的状态:{}", thread_a.getName(), thread_a.getState());
log.info("start之后{},的状态:{}", thread_b.getName(), thread_b.getState());
//主线程睡10s,避免主线程结束子线程还未结束,无法查看日志
Thread.sleep(10000L);
log.info("{},的状态:{}", thread_a.getName(), thread_a.getState());
log.info("{},的状态:{}", thread_b.getName(), thread_b.getState());
}
runnable->waiting/timed_waiting
/**
* 线程状态转换测试
* runnable->waiting/timed_waiting
*/
@Test
public void testState3() throws InterruptedException {
//new:初始化线程后为开始状态
Thread thread_a = new Thread(() -> {
log.info("运行中{},的状态:{}", Thread.currentThread().getName(),
Thread.currentThread().getState());
//TODO 模拟业务代码
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程...a");
log.info("new之后{},的状态:{}", thread_a.getName(), thread_a.getState());
//启动线程
thread_a.start();
//沉睡1s后查看线程状态
Thread.sleep(1000L);
log.info("start之后{},的状态:{}", thread_a.getName(), thread_a.getState());
//主线程睡10s,避免主线程结束子线程还未结束,无法查看日志
Thread.sleep(10000L);
log.info("{},的状态:{}", thread_a.getName(), thread_a.getState());
}
调整sleep(…),为无时间的wait()或者join()
//TODO 模拟业务代码
try {
synchronized (this){
this.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
interrupt()、interrupted()、isInterrupted()
- interrupt()是给线程设置中断标志
- interrupted()是检测中断并清除中断状态(针对当前线程,Thread.interrupted()方法只针对当前线程)
- isInterrupted()只检测中断
线程数据结构
接下来我们从线程Thread的构造函数、类中属性、核心方法等方面分析Thread类
public
class Thread implements Runnable
构造函数
类中核心属性
- MIN_PRIORITY = 1 线程最小优先级
- State {NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; } 枚举JVM线程6中状态
- target 实现Runnbale接口类。执行run()方法时target非空执行target的run()方法
- daemon 守护线程表示,默认false(守护线程在所有线程结束后自动结束,类似Time定时任务)
- threadStatus 线程状态
- tid 线程的ID,默认++threadSeqNumber
- threadSeqNumber 线程的tid初始化参数
- name 线程名字,默认在同步代码块” Thread-”+ threadInitNumber++
- threadInitNumber 线程名字初始话使用参数
- MAX_PRIORITY = 10 线程最大优先级
- NORM_PRIORITY = 5 默认分配优先级
核心方法
我们分析下创建线程并启动的所涉及的核心方法(以带有runnble参数的构造函数为例)
创建含有业务代码的类实现Runnable接口,业务方法写到run()方法中;runnable接口仅有一个抽象方法: public abstract void run();
//实现runnable接口
class MyRunnable implements Runnable {
int num = 0;
@Override
public void run() {
log.info("我正在执行核心业务...");
}
}
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
创建线程并启动线程
/**
* Thread创建和启动测试
*/
public void testThread(){
//创建实现Runnble接口业务类
MyRunnable myRunnable = new MyRunnable();
//创建线程状态为new
Thread thread = new Thread(myRunnable);
//执行线程
thread.start();
}
1) new Thread(myRunnable);
入参为target,线程名称系统默认生成(例:Thread-0,Thread-1,…)
init(…)继续调用底层的统一的init(…)方法
真正的初始化方法如下(省略部分代码)↓↓↓
thread.start();
线程启动后状态从NEW--->RUNNABLE等待CPU资源
线程获得CPU资源后执行run()方法;如果Runnable类型的target非空,执行target类中run方法
备注
方法 | CPU资源 | 释放锁 | 备注 |
yield() | 让出cup资源 | NO | 不释放锁 |
sleep(…) | 让出cup资源 | NO | |
join(…) | 让出cup资源 | yes | 让出cup让其它线程执行 |
wait(…),notiyf(…) | 让出cup资源 | yes | wait后不能自动唤醒,等待其它线程调用notify()或者notifyAll() |
suspend(…),resume(…) | 让出cup资源 | NO | 废弃的方法 |
I/0 | 让出cup资源 | NO | I/0效率比cup低很多 |
LockSupport.park() | 让出cup资源 | NO | 底层二元信号量 |
以上数据结果分析中为无返回值的形式;针对有返回值的形式,线程创建原理如上,只不过target为类FutureTask,继承接口RunnbleFuture,由于RunnableFuture接口又继承Runnble、Future接口,所以FutureTask其实也是Runnble的实现类。……详细待下回分析