美文网首页
Java线程系列——Thread类中线程相关方法

Java线程系列——Thread类中线程相关方法

作者: 禺沫 | 来源:发表于2020-02-27 10:52 被阅读0次

    一、图解方法

    Thread和Object方法概览.png

    二、sleep方法详解:

    1.sleep方法特点

    以下代码运行了两个Thread,等第一个Thread执行完,第二个Thread才能执行,可见sleep方法并不能释放锁。

    public class SleepDontReleaseMonitor implements Runnable {
        @Override
        public void run() {
            syn();
        }
    
        public static void main(String[] args) {
            SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
            new Thread(sleepDontReleaseMonitor).start();
            new Thread(sleepDontReleaseMonitor).start();
        }
    
        private synchronized void syn() {
            System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块");
        }
    }
    

    运行结果:

    线程Thread-0获取到了monitor。
    线程Thread-0退出了同步代码块
    线程Thread-1获取到了monitor。
    线程Thread-1退出了同步代码块

    换成Lock的写法更为明显:

    public class SleepDontReleaseLock implements Runnable {
    
        private static final Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            lock.lock();
            System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁");
            try {
                Thread.sleep(5000);
                System.out.println("线程" + Thread.currentThread().getName() + "已经苏醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
            new Thread(sleepDontReleaseLock).start();
            new Thread(sleepDontReleaseLock).start();
        }
    }
    

    运行结果:

    线程Thread-0获取到了锁
    线程Thread-0已经苏醒
    线程Thread-1获取到了锁
    线程Thread-1已经苏醒

    2.sleep方法的优雅写法

    除了优雅之外,此种方法还能避免传参错误发生的异常。

    public class SleepInterrupted implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(new Date());
                try {
                    TimeUnit.HOURS.sleep(3); //更优雅
                    TimeUnit.MINUTES.sleep(25);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("我被中断了!");
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new SleepInterrupted());
            thread.start();
            Thread.sleep(6500);
            thread.interrupt();
        }
    }
    
    3. sleep方法的总结:

    sleep方法可以让线程进入WAITING状态,并且不占用CPU资源,但是不释放锁。直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。

    常见问题:
    wait/notify、sleep异同?
    相同点:都会进入阻塞状态,而且会相应Interrupt中断
    不同点:
    wait/notify方法需要在同步方法中执行,sleep方法不需要
    wait/notify方法释放锁,sleep方法不释放锁
    wait/notify可以不指定时间,sleep需要传时间参数
    wait/notify属于Object方法,而sleep属于Thread类,所属类不同

    三、join方法详解

    1. join方法的基本使用

    作用:因为新的线程加入了我们,所以我们要等他执行完再出发
    用法:main等待thread1执行完毕,注意谁等谁
    用代码来看一下Join的一般用法:

    public class Join {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
    
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
    
            thread.start();
            thread2.start();
            System.out.println("开始等待子线程运行完毕");
            thread.join();
            thread2.join(); //让主线程等待thread和thread2执行完毕
            System.out.println("所有子线程执行完毕");
        }
    }
    

    运行结果:

    开始等待子线程运行完毕
    Thread-1执行完毕
    Thread-0执行完毕
    所有子线程执行完毕

    结果分析:如果没有Join方法,运行结果一定是thread中的方法最后运行完毕。可见Join方法的效果与用处。

    2.join方法的中断

    子线程加入了主线程,在加入的过程中,到底是谁被中断了呢,子线程还是主线程?
    看一下代码演示:

    public class JoinInterrupt {
        public static void main(String[] args) {
            Thread mainThread = Thread.currentThread();
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        mainThread.interrupt();
                        Thread.sleep(5000);
                        System.out.println("Thread1 finished");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            thread1.start();
            System.out.println("等待子线程执行完毕");
            try { //主线程等待期间被打断,打断的是主线程,而不是子线程
                thread1.join();
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "主线程被中断了");
                e.printStackTrace();
    //            thread1.interrupt();
            }
            System.out.println("子线程已经运行完毕");
        }
    }
    

    运行结果:

    等待子线程执行完毕
    java.lang.InterruptedException
    main主线程被中断了
    子线程已经运行完毕
    at java.lang.Object.wait(Native Method)
    at java.lang.Thread.join(Thread.java:1245)
    at java.lang.Thread.join(Thread.java:1319)
    at threadobjectclasscommonmethods.JoinInterrupt.main(JoinInterrupt.java:29)
    Thread1 finished

    可见运行的是thread1.join(),而实际上被中断的却是主线程,需要再次执行thread1.interrupt(),才能中断thread1

    那么在主线程等待子线程join的时候,主线程的状态是什么呢?代码演示如下:

    public class JoinThreadState {
    
        public static void main(String[] args) throws InterruptedException {
            Thread mainThread = Thread.currentThread();
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                        System.out.println("主线程的运行状态:" + mainThread.getState()); //WAITING
                        System.out.println("子线程的运行状态:" + Thread.currentThread().getState());
                        System.out.println("Thread-0运行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            System.out.println("等待子线程运行完毕");
            thread.join();
            System.out.println("子线程运行完毕");
        }
    }
    

    运行结果:

    等待子线程运行完毕
    主线程的运行状态:WAITING
    子线程的运行状态:RUNNABLE
    Thread-0运行结束
    子线程运行完毕

    可见,在主线程等待子线程join的时候,主线程的状态为WAITIGNG,子线程为RUNNABLE

    3.被封装好的,功能相同的类:CountDownLatch或CyclicBarrier
    4.Join方法源码解析:
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
    
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
    
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
    

    看见,原理主要使用了wait方法,但是wait之后谁来notify呢,原来Thread类在结束之后会主动执行notifyAll,看过原理,那么上面的代码可等价为:

    public class JoinPrinciple {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
    
            thread.start();
            System.out.println("开始等待子线程运行完毕");
    //        thread.join();
            synchronized (thread){
                thread.wait();
            }
            System.out.println("所有子线程执行完毕");
        }
    }
    

    相关文章

      网友评论

          本文标题:Java线程系列——Thread类中线程相关方法

          本文链接:https://www.haomeiwen.com/subject/gzcthhtx.html