线程状态转换以及基本操作

思维导图

java io线程转主线程 java线程的状态转换流程_System

1.新建线程

1.1 Java程序天生就是一个多线程程序,包含了

  • 分发处理发送给JVM信号的线程
  • 调用对象的finalize()方法的线程
  • 清除Reference的线程
  • main线程,用户程序的入口

1.2 如何新建线程

  • 继承Thread类,重写run方法
  • 通过实现Runable接口
  • 通过实现Callable接口

演示:

package bingfang.git_boke_code.Two;

import java.util.concurrent.*;

public class CreateThreadDemo {
    public static void main(String[] args) {
        
        //1.继承Thread
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("继承Thread");
                super.run();
            }
        };
        thread.start();

        //2.实现Runable接口
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("实现Runable接口");
            }
        });
        thread1.start();

        //3.实现callable接口
        ExecutorService service = Executors.newSingleThreadExecutor();
        Future<String> future = service.submit(new Callable(){
            @Override
            public Object call() throws Exception {
                return "通过实现Callable接口";
            }
        });
        try{
            String result = future.get();
            System.out.println(result);
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (ExecutionException e){
            e.printStackTrace();
        }
    }
}

2.线程状态转换

2.1 线程的状态

java io线程转主线程 java线程的状态转换流程_System_02

  • NEW:初始状态,线程被构建,但还没有调用start()方法
  • RUNNABLE:运行状态,Java将操作系统中的就绪和运行两种状态笼统的称为运行中
  • BLOCKED:阻塞状态,表示线程阻塞于锁
  • WAITING:等待状态,线程进入等待状态,表示线程需要等待其他线程做出一些特定动作
  • TIME_WAITING:超时等待状态,该状态不同于WAITING,可以在指定时间自行返回
  • TERMINATED:终止状态,表示该线程已经执行完毕

3.线程基本操作

3.1 interrupted

  • 中断可以理解为线程的一个标志位,它表示一个运行中的线程是否被其他线程执行了中断操作。中断好比其他线程对该线程打了一个招呼。
  • 其他线程可以调用interrupt()方法对其进行中断操作,同时该线程可以用isInterrupted()方法感知其他线程对它的中断操作,从而做出响应。当一个线程在执行Object.wait()/Object.wait(long),Sleep(long),join()/join(long)方法时被中断会抛出Interrupted exception异常
  • 另外,也可以调用ThreadInterrupted()方法对线程进行中断,该方法会清除中断标志位,也就是说在调用isInterrupted()时会返回false。

java io线程转主线程 java线程的状态转换流程_守护线程_03

演示:Main线程去中断sleepThread,sleepThread的中断标志位清零(变为false)并抛出java.lang.InterruptedException: sleep interrupted异常

package bingfang.git_boke_code.Two;

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException{
        //sleepThread 睡眠 1000ms
        final Thread sleepThread = new Thread(){
            @Override
            public void run() {
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                super.run();
            }
        };

        //busyThread一直执行死循环
        Thread busyThread = new Thread(){
            @Override
            public void run() {
                while (true);
            }
        };

        sleepThread.start();
        busyThread.start();
        sleepThread.interrupt();//Main线程去中断sleepThread,sleepThread
                                //的中断标志位清零(变为false)
        busyThread.interrupt();
        while(sleepThread.isInterrupted());
        System.out.println("sleepThread isInterrupted:"+
                sleepThread.isInterrupted());
        System.out.println("busyThread isInterrupted:" +
                busyThread.isInterrupted());
    }
}

输出结果:
sleepThread isInterrupted: false
busyThread isInterrupted: true

3.2 join

  • 概述
  • join方法可以看成线程间协作的一种方式,如果一个线程实例A执行ThreadB.join(),其含义为:当前线程A会等待ThreadB执行完成后(终止后)再继续执行。
  • Thread类除了提供join()方法外,另外还提供了超时等待的方法,如果线程ThreadB在等待时间内还没有结束的话,ThreadA会在超时之后继续执行。(不再继续等下去)

演示:

package bingfang.git_boke_code.Two;

public class JoinDemo {

    public static void main(String[] args) {
        Thread previousThread = Thread.currentThread();
        for(int i = 0; i < 10; i++){
            JoinThread curThread = new JoinThread(previousThread);
            curThread.start();
            previousThread = curThread;
        }
    }

    static class JoinThread extends Thread{
        Thread thread;

        public JoinThread(Thread thread){
            this.thread = thread;
        }

        @Override
        public void run() {
            try{
                thread.join();
                System.out.println(thread.getName()+"terminated");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    /*public static void main(String[] args) {
        Thread previousThread = Thread.currentThread();
        for(int i = 1; i <= 10; i++){
            Thread curThread = new JoinThread(previousThread);
            curThread.start(); //curThread启动后会调用
            			//previousThread的join方法
                        //意思就是说curThread会等待
                        //previousThread执行完再继续执行
            previousThread = curThread;
        }
    }

    static class JoinThread extends Thread{
        private Thread thread;
        
        public JoinThread(Thread thread){
            this.thread = thread;
        }

        @Override
        public void run() {
            try{
                thread.join(); 
                //执行了previousThread的join方法
                //就是说当前线程会等待previousThread
                //执行完后再继续执行
                System.out.println(thread.getName()+"terminated");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }*/
}

运行结果:
mainterminated
Thread-0terminated
Thread-1terminated
Thread-2terminated
Thread-3terminated
Thread-4terminated
Thread-5terminated
Thread-6terminated
Thread-7terminated
Thread-8terminated

3.3 sleep

  • 概念:是Thread的静态方法,作用是让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是当前线程持有锁时,sleep并不会让当前线程失去锁
  • sleep()与Object.wait()方法对比
  • 1.sleep()是Thread的静态方法,而wait是Object的实例方法
  • 2.wait()方法必须在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()没有限制可以在任一情况下使用。另外,wait()方法会释放线程持有的对象锁,使线程重新进入等待池中,等待下一次获取资源。而sleep()只会让出CPU而不会释放掉对象锁。
  • sleep()方法在休息时间完成之后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notify/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片后才会继续执行

3.4 yield

  • 概念:yield()是一个静态方法,一旦执行,它会让当前线程让出CPU,但让出CPU并不代表就不执行了,而是让让出CPU的线程和其他线程再次竞争,如果在下一次竞争中获得了时间片,当前线程依然会继续执行。另外让出的时间片只会分配给当前线程相同优先级的线程。
  • 线程优先级
  • 概念:线程优先级就是线程会或多或少分配一些处理器资源的线程属性
  • 在Java程序中,通过一个整型成员变量Priority来控制优先级,优先级的范围从1-10。在构建线程的时候,可以通过setPriority(int)方法设置线程优先级,默认优先级为5.优先级高的线程比优先级低的线程优先获得处理器时间片。需要注意的是在某些JVM和操作系统上线程规划存在差异,有些操作系统甚至会忽略优先级的设定
  • 另外,sleep()yield()方法,同样都是当前线程会交出处理器资源,但它们不同的是,sleep()交出的时间片其他线程都可以去竞争,即都有机会获得当前线程交出的时间片;而yield()只允许与当前线程具有相同优先度的线程能够获得释放出的时间片

4.守护线程Daemon

  • 守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默守护着一些系统服务,比如垃圾回收线程,JIT线程就可以理解为守护线程。线程可以通过setDeamon(true)将线程设置为守护线程,但需要注意的是设置守护线程必须优先于start()方法,否则会报 java.lang.IllegalThreadStateException错误
  • 与守护线程对应的就是用户线程,用户线程就是系统的工作线程,他会完成整个系统的业务操作。用户线程结束后,系统中就没有对象需要守护了,守护线程也自然会结束。当一个Java程序只有守护线程时,虚拟机就会自然退出
  • 守护线程在退出时并不会执行finally块中的代码,所以释放资源等操作不要放在finally块中,这种操作是不安全的。

演示:

package bingfang.git_boke_code.Two;

public class DaemonDemo {

    public static void main(String[] args) {
        Thread daemonThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try{
                        System.out.println("i am alive");
                        Thread.sleep(500);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }finally {
                        System.out.println("finally block");
                    }
                }
            }
        });

        daemonThread.setDaemon(true);
        daemonThread.start();
        try{
            Thread.sleep(800);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

运行结果:
i am alive
finally block
i am alive

若把deamonThread的sleep时间由Thread.sleep(500);改为Thread.sleep(1000);

运行结果为:

i am alive

明显看出Main线程结束后daemonThread没有执行finally块的内容。

小结:

简单介绍了如何新建线程,线程的6种状态,4种基本操作以及守护线程Deamon的概念。

参考资料

1.  github:Java-concurrency-master

2.《Java并发编程的艺术》