线程实现
继承 Thread 类
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
实现Runnable 接口创建线程
public class MyThread implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
}
}
实现 Callable 接口通过 FutureTask 包装器来创建 Thread 线程
public class CallableDemo implements Callable<String> {
public static void main(String[] args) throws
ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(new CallableDemo());
System.out.println(future.get());
executorService.shutdown();
}
@Override
public String call() throws Exception {
return "返回字符串类型";
}
}
线程状态
线程一共有 6 种状态( new、runnable、blocked、waiting、time_waiting、terminated)
NEW
初始状态,线程被构建,但是还没有调用 start 方法
RUNNABLED
运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中”
BLOCKED
阻塞状态,表示线程进入等待状态,也就是线程,因为某种原因放弃了 CPU 使用权,阻塞也分为几种情况
- 等待阻塞:运行的线程执行 wait 方法,jvm 会把当前线程放入到等待队列
- 同步阻塞 :运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么 jvm 会把当前的 线程放入到锁池中
- 其他阻塞:运行的线程执行 Thread.sleep 或者 t.join 方法,或者发出了 I/O 请求时,JVM 会把当前线程设置为阻塞状态,当 sleep 结束、join 线程终止、io 处理完毕则线程恢复
TIME_WAITING
超时等待状态,超时以后自动返回
TERMINATED
终止状态,表示当前线程执行完毕
在JDK 并发工具包中 常见 yield 让出CPU执行权限, lock锁中常用了TIME_WAITING 锁设置超时释放. 多线程竞争同一把锁下,竞争不到的线程调用park进入等待队列.
Mic提示:启动一个线程前,最好为这个线程设置线程名称,因为这样在使用 jstack 分析程序或者进行问题排查时,就会给开发人员提供一些提示
排查java线程状态: jps命令查询当前java中运行的线程的pid (jps命令工具JDK自带)
根据 获得的pid jstack #{pid} 查询堆栈信息
start()与run()区别
查看start方法源码, 方法中会调用 start0() 的本地方法来启动.
start0()方法又是Thread 的静态块中来注册的registerNatives方法.
registerNatives方法的定义的源码中如下
上图代码地址: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/00cd9dc3c
2b5/src/share/native/java/lang/Thread.c
下面这个代码需要下载 hotspot源码
图中重要的是 new newJavaThread构造中做了什么
线程创建完成之后,开始调用线程启动方法 如下:
线程中断执行方式
官方推荐使用 interrupt 方法 进行中断,interrupt中是 其他线程调用此线程的interrupt 方法,改变了当前线程的某个字段状态标识, 此线程自己通过检查此值有没有发生变化来进行对应操作,可以通过isInterrupted()来判断是否被中断
举例:
第一种情况:
线程A创建开始运行,此时初始化 interrupt 中的标识为false,当B线程调用A线程interrupt 方法时,会改变成true,A线程在代码中自己编写(检查interrupt 状态)代码做相应逻辑处理.
第二种情况:
线程A创建开始运行,此时初始化 interrupt 中的标识为false,此时线程A开始睡眠,sleep操作, 同时B线程调用A线程interrupt 方法时,会抛出 InterruptedException
原因: B线程调用A线程interrupt 方法时,会修改状态值,并且唤醒睡眠中, 睡眠中的线程唤醒后发现睡眠时间还未到达,继续睡眠剩余设定时间,此时sleep方法中会进行is_interrupted()状态判断,判断是否睡眠途中有没有线程试图停止我,如果有 则抛出InterruptedException 异常进行响应.
注意: 阻塞线程唤醒后,如有其他线程通知过中断(true),也不会进行响应,继续干自己的事情,需要手动编写状态判断处理.
thread.interrupt() 方法
这个方法里面,调用了 interrupt0(),这个方法在前面分析start 方法的时候见过,是一个 native 方法,同样,我们找到 jvm.cpp 文件,找到JVM_Interrupt 的定义
接下来看看 Thread::interrupt(thr) 方法
Thread::interrupt 方法调用了 os::interrupt 方法,这个是调用平台的 interrupt 方法,这个方法的实现是在 os_*.cpp文件中,其中星号代表的是不同平台,因为 jvm 是跨平台的,所以对于不同的操作平台,线程的调度方式都是不一样的。我们以 os_linux.cpp 文件为例
set_interrupted(true)实际上就是调用 osThread.hpp 中的set_interrupted()方法,在 osThread 中定义了一个成员属性 volatile jint _interrupted;
多个if判断为: 是否在睡眠,是否被park, 如正在睡眠或阻塞, 进行unpark唤醒或者重新抢锁,如果失败仍然可能被 park