Java Thread基础知识

Java Thread基础知识

java程序中只有一种途径去启动一个线程,即调用Thread类的start()方法。

java中的线程状态

在Thread类中有一个内部的枚举类State描述了线程的各个状态。

public enum State {
        /**
         * 线程创建但未启动时会处于此状态
         */
        NEW,

        /**
         * 线程调用start方法,java虚拟机会启动一个线程,被认为是RUNNABLE状态,
         * 但此时可能在等待操作系统资源,比如等待cpu的处理器执行,
         * 所以可能并没有在操作系统中真正获取到处理器的执行权。
         */
        RUNNABLE,

        /**
         * 获取监视器失败后会处于此状态,比如调用了synchronized方法没有获取到锁时。
         */
        BLOCKED,

        /**
         * 线程在等待另一个线程执行特定操作时,会处于此状态,比如调用了Object.wait(),
         * 则会等待另一个线程调用Object.notify()或Object.notifyAll()方法。
         * 或者调用thread.join()等待被调用线程的执行结束。
         * 调用以下方法会使线程处于此状态
         *   		Object.wait() 
         *			Thread.join()
         *      LockSupport.park()
         * 注意:以上方法都是没有设置设置超时时间
         */
        WAITING,

        /**
         * 相较于WAITING状态,当设置了指定时间时,等待一定时间继续执行,线程处于此状态
         * 调用以下方法会使线程处于此状态
         *   		Object.wait( timeout ) 
				 *   		Thread.sleep( timeout ) 
         *			Thread.join( timeout )
         *      LockSupport.parkNanos
         *			LockSupport.parkUntil
         */
        TIMED_WAITING,

        /**
         * 终止状态,线程执行完成。
         */
        TERMINATED;
    }

线程常用方法

start

调用start()会开始执行一个线程,此时java虚拟机启动一个线程并调用该类的run()方法。

sleep

使当前线程休眠,放弃cpu使用权则或者放弃竞争cpu使用权,则放弃。

注意:此时如果线程持有锁,不会释放。

join

此方法为线程的实例方法。A线程调用B线程的join方法,A线程将处于等待状态,直到B线程执行完后A线程才继续执行。

interrupt

中断线程。将线程从当前的一个状态中释放出来。当调用该方法时会将线程interrupt状态设置为true。比例调用了线程的 wait、join、sleep时,调用interrupt会中断线程的等待状态,继续执行,此时以上方法会抛出InterruptedException异常。或者线程阻塞于IO操作时,调用该方法会将线程中断,关闭IO操作流。

Object的wait和notify

wait

此方法为Object的方法。调用此方法的前提为必须持有当前obj的锁,如果没有获取到锁而调用该方法会抛出java.lang.IllegalMonitorStateException。将当前运行的线程进入等待状态。并且释放obj的锁

大多场景为当线程执行到特定逻辑时需要某些条件,当条件不满足时,调用wait方法进行等待。比如生产者/消费者模式,消费者需要等待生产者的数据准备好后才能执行。

notify/notifyAll

此方法为Object的方法。唤醒等待该obj的锁的线程,调用此方法的前提为必须持有当前obj的锁,如果没有获取到锁而调用该方法会抛出java.lang.IllegalMonitorStateException。此时被唤醒的线程需要重新获取该obj的锁才能运行。notify方法会从等待obj的多个线程中选择一个唤醒,notifyAll会唤醒所有的等待线程。

Thread与Runnbale的关系

开发线程类的两种方式

  • 继承Thread,重写run方法

    public class TestThread extends Thread {
    
        public static void main(String[] args) {
            new TestThread().start();
        }
    
        @Override
        public void run() {
            System.out.println("线程启动");
        }
    }
    
  • 实现Runnable接口,调用new Thread(target).start();

    public class TestRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("线程启动");
        }
    
        public static void main(String[] args) {
            TestRunnable target = new TestRunnable();
            new Thread(target).start();
        }
    }
    

启动线程的方式

上面代码是我们开发线程常用的两种方式,之所以说是开发不是启动,是因为java中启动线程只能通过Thread类的start方法。

public class Thread implements Runnable {
  
  public synchronized void start() {

        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {

            }
        }
    }
  
  	private native void start0();
}

通过源码可以看到 start()调用了start0()方法,start0()是真正让java虚拟机去启动一个线程,此方法为虚拟机内部方法,虚拟机启动线程时会调用run()去执行具体的代码逻辑。所以结论为start方法为让虚拟机真正启动一个线程,而线程的执行逻辑则为run方法。

如有不实,还望指正

posted @ 2020-04-05 20:25  LuxBai  阅读(381)  评论(0编辑  收藏  举报
作者: LuxBai
微信公众号: LuxBai
出处: https://www.cnblogs.com/LuxBai/
本文版权归作者和博客园共有。欢迎转载,但必须保留此段声明,且在文章页面明显位置给出原文连接!