java 线程的创建方式与线程的六种状态切换

线程是 CPU 调度的基本单位。

1. 线程的构造与创建方式

1.1 线程的构造

1.1.1 先了解 Thread 的构造

  • 从构造函数开始,总共有九个
public class Thread implements Runnable {
	/* param1: runnable 任务,param2: 线程访问控制上下文,调用六个参数的 init(),此构造没有对外开放 */
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }
	/* 空构造,调用四个参数的 init() */
	public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    /* param1: runnable 任务,调用四个参数的 init() */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    /* param1: 指定线程组,param2: runnable 任务,调用四个参数的 init() */
    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }
    /* param1:指定线程名称,调用四个参数的 init() */
    public Thread(String name) {
        init(null, null, name, 0);
    }
    /* param1: 指定线程组,param2: 指定线程名称,调用四个参数的 init() */
    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }
    /* param1: runnable 任务,param2: 指定线程名称,调用四个参数的 init() */
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    /* param1: 指定线程组,param2: runnable 任务,param3: 指定线程名称,调用四个参数的 init() */
    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }
    /* param1: 指定线程组,param2: runnable 任务,param3: 指定线程名称,param4: 设置栈空间的大小,调用四个参数的 init() */
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }
}
  • 所有的构造函数,都调用了 init() 方法,四个参数的 init() 底层也调用了六个参数的 init()
  • init() 本质上也是一些属性赋值,只不过ThreadGroup、 AccessControlContext 会进行一些判断
private void init(ThreadGroup g, Runnable target, String name,
                 long stackSize) {
    init(g, target, name, stackSize, null, true);
}
/* 可以理解为真正干活的构造函数 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    /* 线程名字不能为空 */
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

	/* 判断传入的 ThreadGroup == null, true ==> 先获取security的ThreadGroup,失败的话获取currentThread的ThreadGroup,false ==> 直接用 */
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess 不管是哪种线程组 */
    g.checkAccess();

    /* Do we have the required permissions? */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon(); // 是否守护线程
    this.priority = parent.getPriority(); // 优先级
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}       

1.1.2 start() 方法

  • 都知道,线程的启动是调用 start() 开启新线程,那么来看看 start() 源码
public synchronized void start() {
    /* threadStatus 不为0,即已被异常更改 */ 
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* 新线程即将启动,线程组 group++,未启动线程 nUnstartedThreads-- */
    group.add(this);

    boolean started = false;
    try {
    	/* 底层调用 start0(),native 本地调用,先溜为敬,会了再来
    	* private native void start0();
    	*/
        start0();
        started = true;
    } finally {
        try {
        	/* 线程没有启动成功,做相应处理 */	
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

1.1.3 run 方法

  • 简单粗暴,直接调用 target.run(),即 runnable 的 run() 方法。
public void run() {
    if (target != null) {
        target.run();
    }
}

1.1.4 总结

  • Thread 对外开放了七个对外开放,一个不对外的构造方法,底层都是调用 init() 进行数学赋值;
  • start() 方法底层调用 native 修饰的 start0(),即新线程是JVM调用 OS 去生成的;
  • run() 方法直接使用当前线程调用 target.run(),并没有新线程的产生

1.2 线程的四种创建方式

1.2.1 继承 Thread 类

public class ExtendsThreadsTest{
    static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程名称: " + Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread01 thread01 = new Thread01();
        thread01.start();
        Thread.sleep(100);
        thread01.run();
        thread01.run();
        thread01.run();
        System.out.println("====== 华丽分割线 =====");
        thread01.start();

    }
}
  • 同一个 thread 实例,不能 start 两次,但可以 run 多次
    • Thread 类有 threadStatus 状态,更改后(不为0)则代表已经 start()
    • if (threadStatus != 0) throw new IllegalThreadStateException();- 在这里插入图片描述

1.2.2 实现 Runnable 接口

  • 使用 Thread 的构造函数,将 runnable 作为参数传入
public class RunnableTest {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("我是 runnable 方式创建的线程: " + Thread.currentThread().getName());
            }
        };
        Thread thread = new Thread(runnable);
        thread.run();
        System.out.println("thread线程是否存活: " + thread.isAlive());
        thread.start();
        System.out.println("thread线程是否存活: " + thread.isAlive());
        Thread.sleep(1000);
        System.out.println("thread线程是否存活: " + thread.isAlive());
        thread.run();
    }
}
  • 最后的 thread.run() 不会有任何输出,因为 thread.start() 执行完毕之后,会清除 thread 的一些信息。可以 debugger 查看 thread 的区别
  • 与 2.1.1 的 thread01.run 对比,thread01 自身实现了 run 方法,可以被直接调用
    在这里插入图片描述

1.2.3 Callable和Future搭配

  • 1、定义一个类实现Callable接口,并重写 call() 方法
  • 2、创建Callable实现类的实例,使用FutureTask类来包装Callable对象
  • 3、使用 FutureTask 对象作为Thread对象的target创建并启动线程(FutureTask< V > implements RunnableFuture< V >,public interface RunnableFuture< V > extends Runnable, Future< V >)
  • 4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableFutureTest {
    static class CallableTest implements Callable {
        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for(int i = 1; i <= 10; i++) {
                sum += i;
            }
            return sum;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest callableTest = new CallableTest();
        FutureTask<CallableTest> callableTestFutureTask = new FutureTask<>(callableTest);
        Thread thread = new Thread(callableTestFutureTask);
        thread.start();
        callableTestFutureTask.get();
    }
}

在这里插入图片描述

1.2.4 线程池

2. 线程的状态切换

2.1 线程的状态

  • Thread 内有一个枚举类 State ,用于存储线程的状态,共有 6 种
public enum State {
    /* 刚创建,尚未启动的状态 */
    NEW,
    
    /* 可运行(就绪)状态,等待 CPU 分配资源 */
    RUNNABLE,
    
    /* 阻塞状态:与 WAITING 的区别,BLOCKED 是被锁住的等待 */
    BLOCKED,
    
	/* 调用以下方法,state 变成 WAITING 
	*  Object.wait、Thread.join、LockSupport.park,等待特定条件到达时变成 RUNNABLE,如Object.wait() 等待 Object.notify() or Object.notifyAll() 唤醒
	*/
    WAITING,
    
    /* 具有等待时间的等待线程状态,调用以下方法进入此状态
     * Thread.sleep(long time)、Object.wait(long time)
     * LockSupport.parkNanos、LockSupport.parkUntil
     */
    TIMED_WAITING,
    
    /* 线程终止状态,线程已完成执行 */
    TERMINATED;
}

2.2 线程状态切换

  • 代码演示 jdk1.8 六种线程状态,看 print 有大概的执行过程
public class ThreadStateTest {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        ReentrantLock lock = new ReentrantLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程正在执行 In run, --> Now state is " + Thread.currentThread().getState());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("============= I am wait =============");
                synchronized (obj) {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                lock.lock();
                try {
                    System.out.println("========= lock success =========");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        };

        Thread t1 = new Thread(runnable, "t1");
        System.out.println("线程刚new完 --> Now state is " + t1.getState());

        t1.start();
        Thread.sleep(500);
        System.out.print("t1.start() 正在执行sleep() -->  " );
        System.out.println("Now state is " + t1.getState());

        Thread.sleep(1000);
        System.out.print("t1.start() 正在执行wait() --> " );
        System.out.println("Now state is " + t1.getState());

        synchronized (obj) {
            lock.lock();
            try {
                obj.notify();
                Thread.sleep(1000);
                System.out.println("notify thread, 线程正在尝试 Lock.lock() --> Now state is " + t1.getState());
                Thread.sleep(1000);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        t1.join();
        System.out.print("t1.start() 执行结束 --> " );
        System.out.println("Now state is " + t1.getState());
    }
}

在这里插入图片描述

2.3 线程状态见切换

  • 可简单总结为下图:
    在这里插入图片描述
  • NEW ==> RUNNABLE:调用 start() 即可。
  • RUNNABLE ==> TERMINATED :线程正常执行结束。
  • RUNNABLE ==> BLOCKED :多线程争抢锁,没抢到的就阻塞即BLOCKED
  • BLOCKED ==> RUNNABLE :多线程争抢锁,抢到的就从阻塞变回 RUNNABLE
  • RUNNABLE ==> WAITING:Object.wait()、LockSupport.park() 等,
  • WAITING ==> RUNNABLE:Object.notify()、LockSupport.unpark() 等,
  • RUNNABLE ==> TIMED_WAITING:Object.wait(long time)、Thread.sleep(long time)

3. 总结

  • 线程池的四种创建方式,底层都是调用new Thread() 生成的新线程
  • 线程的六种状态,是在 Thread 的内置枚举类 State 定义的,可通过 getState() 获取线程当前状态。
  • 没事可多看看源码,英文表达的意思有时候更加传神。
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页