线程API

线程方法API

  • start()

    启动线程,线程以异步的方式来执行;

    启动一次后,不可以再重复调用,否则会出现异常

  • run()

    启动异步线程后,线程run方法中的内容;

    如果直接调用run方法,那么run方法就相当于是一个普通方法,被他所在的那个线程调用,也就是同步执行,不会异步执行

  • state

    线程状态

  • sleep

    sleep方法,会让线程进入睡眠,此时线程的状态是time waiting;

    其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep线程会抛出InterruptedException,sleep线程继续执行;

    sleep结束的线程未必会立即得到执行;

    建议使用TimeUnit的sleep代替Thread的sleep来获得更好的可读性;

  • yield

    调用yield方法会让当前线程从running进入runnable就绪状态,然后调度其他线程;

    注意:调用了yield方法,并不会切换到别的线程,具体的线程依赖于操作系统的任务调度

  • 线程优先级setPriority

    线程优先级会提示cpu调度器优先调度该线程;

    如果在cpu比较忙时,优先级高的线程会获得更多的时间片,但cpu闲时,优先级几乎没有作用

  • isInterrupted

    判断是否被打断,不会清除打断标记

  • isAlive

    判读线程是否还存活

  • interrupt

    打断线程,如果线程正在sleep, wait, join会导致被打断的线程抛出InterruptedException,并清除打断标记;

    如果打断正在运行的线程,则会设置打断标记

    	public static void main(String[] args) {
            Thread thread = new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            thread.interrupt();	// 打断sleep的线程,会清除标记;打断running的线程,打断标记为false
            System.out.println("线程状态:" + thread.getState() + " 打断标记:" + thread.isInterrupted());
        }
    

    输出结果:

    java.lang.InterruptedException
    	at java.lang.Thread.sleep(Native Method)
    	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    	at com.mylearn.thread.Test6.lambda$main$0(Test6.java:9)
    	at com.mylearn.thread.Test6$$Lambda$1/0x0000000000000000.run(Unknown Source)
    	at java.lang.Thread.run(Thread.java:823)
    线程状态:RUNNABLE 打断标记:false
    

    如果打断的线程是running的状态,打断标记会被置为true,但也仅仅是将打断标记置为了true,线程仍然会继续执行。

    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while(true){
                if(Thread.currentThread().isInterrupted()){
                    break;
                }
                System.out.println(1);
            }
        });
        thread.start();
        thread.interrupt();
        System.out.println("线程状态:" + thread.getState() + " 打断标记:" + thread.isInterrupted());
    }
    
  • interrupted

    判断当前线程是否被打断,会清除打断标记

  • currentedThread

    获取当前正在执行的线程

  • join

    a线程调用b线程的join方法,a线程会等待b线程执行完后再继续执行。

    static int res = 0;
    
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res = 10;
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(res);
    }
    

    如果有多个线程时,主线程可以调用多个线程的join方法,使得main线程等待所有线程都执行完再继续执行main线程。

    	static int r1 = 0;
        static int r2 = 0;
        public static void main(String[] args) {
            Thread thread1 = new Thread(()->{
                r1 = 10;
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            Thread thread2 = new Thread(()->{
                r2 = 20;
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            thread1.start();
            thread2.start();
            long start = System.currentTimeMillis();
    
            try {
                thread1.join();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() - start);
        }
    

    join方法也可以指定时间来等待。

两阶段终止模式

概念

在一个线程T1中,如何优雅的终止线程T2?

错误思路

  1. 使用线程的stop方法

    stop方法会真正杀死进程,如果这时线程锁住了共享资源,那么当它被杀死后,再也没机会释放锁,其他线程永远无法获取锁

  2. 使用System.exit(1)

    这种做法会让整个程序都停止。

正确思路

	public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
        twoPhaseTermination.start();
        TimeUnit.SECONDS.sleep(5);
        twoPhaseTermination.stop();
    }

    static class TwoPhaseTermination {
        private Thread monitorThread;

        public void start() {
            monitorThread = new Thread(() -> {
                while (true) {
                    Thread current = Thread.currentThread();
                    if (current.isInterrupted()) {
                        System.out.println("料理后事");
                        break;
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println("running monitor");
                    } catch (InterruptedException e) {
                        current.interrupt();
                    }
                }
            });
            monitorThread.start();
        }

        public void stop() {
            monitorThread.interrupt();
        }
    }

打断park的线程

可以使用工具类LockSupport来使线程暂停,可以使用interrupt打断正在park的线程,被打断的park线程无法再次park,除非清除打断标记。

	public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("park...");
            LockSupport.park();
            System.out.println("unpark");
            LockSupport.park();
            System.out.println("打断标记: " + Thread.currentThread().isInterrupted());
            System.out.println("打断标记: " + Thread.interrupted());
            LockSupport.park();

        });
        t1.start();

        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
    }

过时不推荐的方法

  • stop
  • suspend
  • resume

主线程和守护线程

设置守护线程: setDaemon(true)

上一篇:HTML协议


下一篇:2021-10-19