线程Thread基础知识

    技术2022-07-12  88

    目录

    一、线程的定义

    1、继承Thread类

    2、实现Runnable接口

    二、Thread的常用方法

    1、join(加入)

    2、yield(让步)

    3、setPriority(设置优先级)

    4、interrupt(中断)

    三、线程的六种状态

    1、new(新建)

    2、RUNNABLE(运行)

    3、TERMINATED(终止)

    4、WAITING(无限期等待)

    5、TIMED_WAITING(限期等待)

    6、BLOCKED(阻塞)


    一、线程的定义

    Java中的线程,简单来说,就是一段顺序执行的代码,是CPU执行的最小单位。新建并启动一个线程,本质都是新建一个Thread或Thread子类的对象,然后调用其start方法,最终执行的代码块位于run方法内(start方法被调用后,JVM会在某一刻自动执行run方法),start和run方法如下:

    /** * 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() { /** * 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(); } }

    run方法中,target是Runnable接口的实例。判断如果target不为空,那么就执行其run方法;否则就什么都不执行。鉴于这种判断逻辑,新建一个新的有具体执行代码的执行线程有如下两种方式:

    1、继承Thread类

    声明一个类继承Thread类,重写run方法();调用该类无参构造方法,新建该类实例;调用其start方法

    2、实现Runnable接口

    (优先使用该种方式,原因:多个线程可以同时使用,达到资源共享;java只支持单继承,但可以实现多接口)

    声明一个类RunnableImpl实现Runnable接口,重写run方法;调用Thread类有参构造方法,将RunnableImpl实例作为入参创建Thread实例;调用其start方法

    二、Thread的常用方法

    1、join(加入)

    在线程A中调用线程B的join方法后,直到B执行结束后,位于join方法后的A的代码才会被执行

    /** * Waits for this thread to die. * * <p> An invocation of this method behaves in exactly the same * way as the invocation * * <blockquote> * {@linkplain #join(long) join}{@code (0)} * </blockquote> * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public final void join() throws InterruptedException { join(0); }

    首先看不使用join方法的情况下,在main线程中启动一个线程t,此时两个线程并发执行,代码如下:

    public class TestJoin { public static void main(String[] args) { MyThread t = new MyThread("t"); t.start(); for (int i = 0; i < 100; i++) { System.out.println("I am main thread"); } } } class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("I am " + getName()); } } }

    部分输出结果如下,可以看出main线程和t线程在交替执行

    I am main thread I am t I am main thread I am main thread I am main thread I am main thread I am main thread I am main thread I am t I am t

    此时如果在启动t线程后,接着调用t的join方法后,main线程只有等到t线程run方法执行结束后,才会执行后面的代码

    public class TestJoin { public static void main(String[] args) { MyThread t = new MyThread("t"); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println("I am main thread"); } } } class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("I am " + getName()); } } }

    部分输出结果如下,线程t先执行完,然后线程main才会执行

    I am t I am t I am t I am t I am t I am t I am main thread I am main thread I am main thread I am main thread I am main thread I am main thread I am main thread

    注意:同时调用t1和t2的join方法后,t1和t2的执行情况是随机的,互不干扰。且只有等到t1和t2都执行完,main方法后面的代码才会执行

    public static void main(String[] args) { MyThread t1 = new MyThread("t1"); MyThread t2 = new MyThread("t2"); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println("I am main thread"); } }

    2、yield(让步)

    在当前线程中调用yield方法,表示告诉调度程序,当前线程愿意让出当前分到的时间片,这样其他线程就可以获取到更多的执行时间片

    /** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();

    首先正常启动两个线程t1、t2后,输出结果一般是t1执行一段时间,t2执行一段时间

    public class TestYield { public static void main(String[] args) { MyThread2 t1 = new MyThread2("t1"); MyThread2 t2 = new MyThread2("t2"); t1.start(); t2.start(); } } class MyThread2 extends Thread { public MyThread2(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("I am " + getName() + " , i = " + i); } } }

    部分输出结果,可以看出结果是很随机的

    I am t1 , i = 0 I am t2 , i = 0 I am t1 , i = 1 I am t1 , i = 2 I am t1 , i = 3 I am t1 , i = 4 I am t1 , i = 5 I am t2 , i = 1 I am t1 , i = 6 I am t2 , i = 2 I am t1 , i = 7

    如果在run方法中判断当i % 10 == 0条件成立时就调用当前线程的yield,那么当某个线程的i满足该条件时,下一个获取CPU时间片的一定是另外一个线程

    public class TestYield { public static void main(String[] args) { MyThread2 t1 = new MyThread2("t1"); MyThread2 t2 = new MyThread2("t2"); t1.start(); t2.start(); } } class MyThread2 extends Thread { public MyThread2(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("I am " + getName() + " , i = " + i); if (i % 10 == 0) { Thread.yield(); } } } }

    3、setPriority(设置优先级)

    优先级越高,理论上线程获取到的CPU执行时间片越多。在Thread类中有如下三个关于优先级的静态变量,最小值为1,最大值为10,默认值为5

    /** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; ** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;

    如下代码,将t2的优先级在默认值上加3,可以使t2获取到更多的执行时间片

    public class TestPriority { public static void main(String[] args) { MyThread3 t1 = new MyThread3("t1"); MyThread3 t2 = new MyThread3("t2"); t2.setPriority(Thread.NORM_PRIORITY + 3); t1.start(); t2.start(); } } class MyThread3 extends Thread { public MyThread3(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("I am " + getName() + " , i = " + i); } } }

    注意:线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不用理会Java线程对于优先级的设定。

    4、interrupt(中断)

    根据字面意思,很容易将该方法理解为中断线程。其实Thread对象的interrupt()方法并不会中断线程的运行,它的作用仅仅是为线程设定一个状态而已,即标明线程是中断状态,这样线程的调度机制或我们的代码逻辑就可以通过判断这个状态做一些处理,比如sleep()方法会抛出异常,或是我们根据isInterrupted()方法判断线程是否处于中断状态,然后做相关的逻辑处理。

    一个线程默认的中断状态为false,且跟下面要讲的线程的六种状态无任何关系,看如下代码:

    public static void testInterupt1() { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { for (int j = 0; j < Integer.MAX_VALUE; j++) { if (j == (Integer.MAX_VALUE - 1)) { System.out.println(j); } } } } }); //NEW状态下 System.out.println("线程状态为:" + thread.getState() + ",中断状态为:" + thread.isInterrupted()); //RUNNABLE状态下 thread.start(); System.out.println("线程状态为:" + thread.getState() + ",中断状态为:" + thread.isInterrupted()); //中断线程 thread.interrupt(); System.out.println("已中断线程"); System.out.println("线程状态为:" + thread.getState() + ",中断状态为:" + thread.isInterrupted()); }

    输出结果如下:

    线程状态为:NEW,中断状态为:false 线程状态为:RUNNABLE,中断状态为:false 已中断线程 线程状态为:RUNNABLE,中断状态为:true 2147483646 2147483646 2147483646 2147483646 2147483646

    可以看出,新建一个线程后,它的中断状态为false;当调用线程的start()方法后,中断状态也为false;只有当调用了线程的interrupt()方法后,其中断状态才为true。但需要注意,此时线程没有被终止,仍然在运行,打印出的状态为RUNNABLE以及for循环依旧在运行都可以证明这一点。

    通过上面的例子,再根据如下部分官方注释,需要注意的一点是:如果当前线程由于诸如sleep()方法导致阻塞或等待,那么调用interrupt()方法会抛出InterruptedException异常,并且会清空线程的中断状态,置为默认的false。

    /** * Interrupts this thread. * * <p> If this thread is blocked in an invocation of the {@link * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link * Object#wait(long, int) wait(long, int)} methods of the {@link Object} * class, or of the {@link #join()}, {@link #join(long)}, {@link * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)}, * methods of this class, then its interrupt status will be cleared and it * will receive an {@link InterruptedException}. * * <p> If none of the previous conditions hold then this thread's interrupt * status will be set. </p> * * <p> Interrupting a thread that is not alive need not have any effect. * * @throws SecurityException * if the current thread cannot modify this thread * * @revised 6.0 * @spec JSR-51 */ public void interrupt()

    验证代码如下:

    public static void testInterupt2() throws Exception { Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100000L); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程是否处于中断状态" + Thread.currentThread().isInterrupted()); } } }); thread.start(); thread.interrupt(); }

    输出结果如下:

    java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at cn.yxz.thread.ThreadMethodTest$2.run(ThreadMethodTest.java:64) at java.lang.Thread.run(Thread.java:748) 线程是否处于中断状态false

     

    三、线程的六种状态

    1、new(新建)

    初始状态,线程被构建,但是还没有调用start()方法

    /** * Thread state for a thread which has not yet started. */ NEW static void state_new() { Thread thread = new Thread(); System.out.println(thread.getState());/输出结果为NEW }

    2、RUNNABLE(运行)

    包括了操作系统线程状态中的Ready(就绪)和Running(运行中),也就是处于此状态的线程有可能正在执行,也有可能正在等待CPU为它分配执行时间片。我们常说的多线程并发,这些线程就是都处在Runnable状态。

    /** * 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. */ RUNNABLE /** * 当JVM在执行run方法体时(注意:不要调用sleep方法),此时线程状态为RUNNABLE * 当run方法体执行结束后,变成TERMINATED */ static void state_runnable() { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < Integer.MAX_VALUE; i++) { for (int j = 0; j < Integer.MAX_VALUE; j++) { } } } }); thread.start(); while (true) { System.out.println(thread.getState()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }

    输出结果如下

    RUNNABLE TERMINATED TERMINATED

    3、TERMINATED(终止)

    run方法体执行结束的线程状态,实验代码如上

    /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED

    4、WAITING(无限期等待)

    处于这种状态的线程不会被分配CPU时间片,它们要等待被其它线程显式地唤醒,可以通过调用无参的join()方法实现(join()的内部实现就是当线程执行结束后调用notifyAll()方法)。

    /** * 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

    如下代码,在main线程中调用thread的join方法后,main线程就一直处于WAITING状态

    static void state_waiting() { //获取main线程 Thread mainThread = Thread.currentThread(); //新建线程 Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("main线程状态:" + mainThread.getState());; try { Thread.sleep(1000); } catch (InterruptedException e) { } } } }); thread.start(); try { //join方法表示直到thread线程执行结束后,main线程才会继续执行 thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }

    输出结果如下:

    main线程状态:WAITING main线程状态:WAITING main线程状态:WAITING

    5、TIMED_WAITING(限期等待)

    处于这种状态的线程也不会被分配CPU时间片,但是无须等待被其它线程显式地唤醒,在一定时间之后它们会由系统自动唤醒,可以通过调用有参的join(long)方法实现。

    /** * 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

    代码如下,注意此时用的是join的有参方法

    static void state_timed_waiting() { //获取main线程 Thread mainThread = Thread.currentThread(); //新建线程 Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("main线程状态:" + mainThread.getState()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } }); thread.start(); try { //join(5000)方法表示等待thread线程执行5000ms后,main线程就会继续执行 thread.join(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main线程继续执行"); }

    输出结果

    main线程状态:TIMED_WAITING main线程状态:TIMED_WAITING main线程状态:TIMED_WAITING main线程状态:TIMED_WAITING main线程状态:TIMED_WAITING main线程继续执行 main线程状态:TERMINATED main线程状态:TERMINATED main线程状态:TERMINATED

    6、BLOCKED(阻塞)

    “阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待获取一个锁,而“等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域时,线程就会进入阻塞状态。

    /** * 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}. */ BLOCKED,

    验证代码如下

    static void state_block() throws Exception { Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (ThreadStateTest.class) { while (true) { } } } }); thread1.start(); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (ThreadStateTest.class) { while (true) { } } } }); thread2.start(); while (true) { System.out.println("thread1 state = " + thread1.getState()); System.out.println("thread2 state = " + thread2.getState()); Thread.sleep(1000); } }

    输出结果如下,thread2始终处于BLOCKED状态:

    thread1 state = RUNNABLE thread2 state = BLOCKED thread1 state = RUNNABLE thread2 state = BLOCKED thread1 state = RUNNABLE thread2 state = BLOCKED

     

    Processed: 0.008, SQL: 9