线程的状态
在操作系统层面有五种状态 初始状态、可运行状态、运行状态、休眠状态和终止状态
初始状态 指的是编程语言层面创建了线程 就是java语言之new了一个线程 但是操作系统并没有真正的把这个线程创建出来
可运行状态 指的是 操作系统以及把线程创建出来了 但是还没分配给cpu执行这个线程
运行状态 指的是 操作系统把线程分配给了cpu 这样 线程就会变成运行状态
休眠状态 指的是 我们调用了阻塞的api或者这个线程需要等别的线程执行完才能执行 这样的情况下就是休眠状态
终止状态 指定是 线程发生了异常或者线程的逻辑执行完
查看进程线程的方法
windows
任务管理器可以查看进程和线程数,也可以用来杀死进程
tasklist 查看进程
taskkill 杀死进程
linux
ps -fe 查看所有进程
ps -fT -p 查看某个进程(PID)的所有线程
kill 杀死进程
top 按大写 H 切换是否显示线程
top -H -p 查看某个进程(PID)的所有线程
Java
jps 命令查看所有 Java 进程
jstack 查看某个 Java 进程(PID)的所有线程状态
jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)、
java的线程有六个状态
在Thread类中的枚举State可以看到
public enum State { /** * Thread state for a thread which has not yet started. * 尚未启动的线程的线程状态 */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ /** *可运行线程的线程状态。可运行对象中的线程 *状态在Java虚拟机中执行,但它是可能的 *等待操作系统的其他资源 *如处理器。 */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. * 正在等待监视器锁的阻塞线程的线程状态。 一个处于阻塞状态的线程正在等待一个监视器锁 *输入一个同步的块/方法或 *调用后重新输入一个同步的块/方法 * {@link Object#wait() Object.wait}。 */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * 等待线程的线程状态。 一个线程正在等待状态,因为它调用了一个 * following methods: * 请尝试一下方法 * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *指定等待时间的等待线程的状态。 *线程由于调用其中一个线程而处于定时等待状态 *以下方法具有指定的正等待时间: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. * 终止线程的线程状态。 *线程已经完成执行。 */ TERMINATED; }
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
NEW表示只创建了线程对象 但是还没执行start方法
RUNNABLE表示可运行状态 操作系统以及创建了跟这个java相关的线程但是还没给cpu分配
BLOCKED等待状态
WAITING阻塞状态
TIMED_WAITING有时限等待状态
在操作系统层面,Java 线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态,就是前面说的休眠状态。也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永远没有 CPU 的使用权。
java创建线程的实现方法
方式1:使用 Thread类或继承Thread类
// 创建线程对象 Thread t = new Thread() { public void run() { // 要执行的任务 } };
方式2:实现 Runnable 接口配合Thread
把【线程】和【任务】(要执行的代码)分开 Thread 代表线程 Runnable 可运行的任务(线程要执行的代码) Runnable runnable = new Runnable() { public void run(){ // 要执行的任务 } }; // 创建线程对象 Thread t = new Thread( runnable );
方式3:使用有返回值的 Callable
class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { return new Random().nextInt(); } } //创建线程池 ExecutorService service = Executors.newFixedThreadPool(10);
方式4:使用 lambda
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
这四种实现方式最终还是去通过new Thread()创建线程 调用Thread的start启动线程 最终都会调用到Thread的run方法
为什么不直接调用run方法而是调用start方法
在Thread类中有一个
private static native void registerNatives(); static { registerNatives(); }
这样的代码 在加载对象的时候会执行registerNatives方法 这个方法在jvm源码中是去让操作系统创建一个线程创建完线程会让这个线程跟java对象做一个绑定 然后进入阻塞状态 等Thread 执行了start方法之后会唤醒这个线程然后最终会调用到Thread类的run方法 这里也说明了为什么java的线程创建跟回收是一个重操作了
Java线程属于内核级线程
JDK1.2——基于操作系统原生线程模型来实现。Sun JDK,它的Windows版本和Linux版本都使用一对一的线程模型实现,一条Java线程就映射到一条轻量级进程之中。
内核级线程(Kernel Level Thread ,KLT):它们是依赖于内核的,即无论是用户进程中的线程,还是系统进程中的线程,它们的创建、撤消、切换都由内核实现。
用户级线程(User Level Thread,ULT):操作系统内核不知道应用线程的存在。
协程
协程是一个基于线程之上的 又比线程更加轻量级的存在 线程不是被操作系统内核管理 完全是由程序控制也就是在用户态执行 go语言就是使用的协程也是因为它使用的是协程所以在高并发场景下很强 可以把协程理解为 在线程中的线程 这个线程的创建完全是应用程序控制 可以理解为协程是一个逻辑线程
等待唤醒机制
等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒。
public class WaitDemo { private static Object lock = new Object(); private static boolean flag = true; public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { synchronized (lock){ while (flag){ try { System.out.println("wait start ......."); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("wait end ....... "); } } }).start(); new Thread(new Runnable() { @Override public void run() { if (flag){ synchronized (lock){ if (flag){ lock.notify(); System.out.println("notify ......."); flag = false; } } } } }).start(); }
LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); System.out.println("唤醒parkThread"); LockSupport.unpark(parkThread); } static class ParkThread implements Runnable{ @Override public void run() { System.out.println("ParkThread开始执行"); LockSupport.park(); System.out.println("ParkThread执行完成"); } }
Java线程的中断机制
Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制。中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。被中断的线程拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。
API的使用
interrupt(): 将线程的中断标志位设置为true,不会停止线程
isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
public class ThreadInterruptTest { static int i = 0; public static void main(String[] args) { System.out.println("begin"); Thread t1 = new Thread(new Runnable() { @Override public void run() { while (true) { i++; System.out.println(i); //Thread.interrupted() 清除中断标志位 //Thread.currentThread().isInterrupted() 不会清除中断标志位 if (Thread.currentThread().isInterrupted() ) { System.out.println("========="); } if(i==10){ break; } } } }); t1.start(); //不会停止线程t1,只会设置一个中断标志位 flag=true t1.interrupt(); }
public class StopThread implements Runnable { @Override public void run() { int count = 0; while (!Thread.currentThread().isInterrupted() && count < 1000) { System.out.println("count = " + count++); } System.out.println("线程停止: stop thread"); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThread()); thread.start(); Thread.sleep(5); thread.interrupt(); }
使用中断机制时一定要注意是否存在中断标志位被清除的情况
sleep 期间能否感受到中断
修改上面的代码,线程执行任务期间有休眠需求
@Override public void run() { int count = 0; while (!Thread.currentThread().isInterrupted() && count < 1000) { System.out.println("count = " + count++); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程停止: stop thread");
处于休眠中的线程被中断,线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。这样就会导致while条件Thread.currentThread().isInterrupted()为false,程序会在不满足count < 1000这个条件时退出。如果不在catch中重新手动添加中断信号,不做任何处理,就会屏蔽中断请求,有可能导致线程无法正确停止。
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); //重新设置线程中断状态为true Thread.currentThread().interrupt();
sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位
wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位