使用join,daemon,interrupt优雅结束线程

认识join

简单理解: 被创建出来的线程t1, 如果t1.join()到主线程,那么需要等到t1线程执行完成,主线程才能继续执行.

public class ThreadJoin {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            IntStream.range(1, 1000)
                    .forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
        });
        Thread t2 = new Thread(() -> {
            IntStream.range(1, 1000)
                    .forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
        });
        //必须先启动,然后才能join
        t1.start();
        t2.start();
        //这里t1和t2都是join到主线程(main)
        //所以t1 和t2 还是同时执行的,
        t1.join();
        t2.join();

        //当t1和t2执行完成,才能继续执行下面的代码
        Optional.of("All of tasks finish done.").ifPresent(System.out::println);
        IntStream.range(1, 1000)
                .forEach(i -> System.out.println(Thread.currentThread().getName() + "->" + i));
    }
}

上述代码中t1和t2交替执行输出,执行完成,才是main线程输出

当然可以设置等待超时时间

t1.start(); 
//主线程等待t1线程先执行100毫秒,20纳秒之后,如果t1还没有执行完成, //主线程和t1一起并发执行 
t1.join(100,10);

可以使用join实现主线程的永不退出

Thread.currentThread().join();

认识demoan

简单认识: 由父线程创建出来的线程,在启动之前设置为 t.setDaemon(true); 父线程退出,该后台线程t也会跟着退出

public class DaemonThread {

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread() {

            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + " running");
                    Thread.sleep(100000);
                    System.out.println(Thread.currentThread().getName() + " done.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t.setDaemon(true);
        t.start();

        //父线程五秒之后退出,创建的t 守护线程会一起退出
        Thread.sleep(5_000);
        System.out.println(Thread.currentThread().getName());
    }
}

输出:
Thread-0 running
main

父线程等待五秒之后结束,t线程也会跟着主线程一起结束.

认识interrupt(),方法

简单认识: 给线程标记一个打断的标志,并不是结束线程,当线程被sleep(),wait(),join() 会抛出 InterruptedException 异常,抛出之后,该线程的打断标志复原

public class InterruptTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("收到标志>>>> 线程被打断");
                System.out.println("异常抛出后t1线程打断标志>>>>" + Thread.interrupted());
                e.printStackTrace();
            }
        });

        t1.start();
        //保证t1线程已经在执行
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //输出打断前的标志
        System.out.println("t1打断前的标志>>>>>" + t1.isInterrupted());
        //打断线程
        t1.interrupt();
    }
}

输出:
t1打断前的标志>>>>>false
java.lang.InterruptedException: sleep interrupted
收到标志>>>> 线程被打断

at java.lang.Thread.sleep(Native Method)

异常抛出后t1线程打断标志>>>>false

注意:一但异常抛出,打断标志会复原

对t.join() 的时候,打断的应该是父线程
 //创建一个线程
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {

                }
            }
        };
        //开启线程
        t.start();

        //保存一下父线程
        Thread main = Thread.currentThread();
        //开启第二个线程,在里面打断join方法上的线程
        Thread t3 = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //打断主线程
                main.interrupt();
                System.out.println("interrupt");
            }
        };

        t3.start();
        try {
            //注意t.join,其实执行方法还是主方法
            t.join();
        } catch (InterruptedException e) {
            System.out.println("收到打断信号>>>");
            e.printStackTrace();
        }

        System.out.println("main线程继续执行");
    }

输出:
java.lang.InterruptedException
interrupt
收到打断信号>>>
main线程继续执行

原因: t.join()到父线程是main线程. 需要对main线程打断,才能捕获到打断异常

使用这三个方法,合理的结束线程

场景: 如果一个任务数据库查询处理数据,在规定的时间内不能完成,需要结束这个线程.

在主线程中(可以理解为main线程)创建一个执行线程executeThread,在执行线程中在创建一个daemon后台线程runnerThread,去执行真正的任务.将
runnerThread.join()到父线程中,当一定时间后,处理没有完成,执行executeThread.interrupt().打断执行线程,捕获到异常,执行线程就可以往下执行,不需要等待runnerThread()执行完成.

public class ThreadService {

    //执行线程
    private Thread executeThread;

    //判断有没有在规定时间内结束
    private volatile boolean finished = false;

    public void execute(Runnable task) {
        executeThread = new Thread() {
            @Override
            public void run() {
                Thread runner = new Thread(task);
                runner.setDaemon(true);

                runner.start();
                try {
                    runner.join();
                    //正常执行完成
                    finished = true;
                } catch (InterruptedException e) {
                    //如果捕获到异常说明,executeThread被打断了.
                    //流程继续往下走,及不需要等待runner执行结束了
                    //e.printStackTrace();
                }
            }
        };
        //执行线程启动
        executeThread.start();
    }

    public void shutdown(long mills) {
        long currentTime = System.currentTimeMillis();
        while (!finished) {
            if ((System.currentTimeMillis() - currentTime) >= mills) {
                System.out.println("任务超时,需要结束他!");
                executeThread.interrupt();
                break;
            }
        }

        finished = false;
    }
}

测试用例:

 public static void main(String[] args) {

        ThreadService2 service = new ThreadService2();
        long start = System.currentTimeMillis();
        service.execute(() -> {
            try {
                //这边模拟处理任务
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //等待10m,任务正常结束
        //service.shutdown(10000);

        //等待四秒,任务被强制结束
        service.shutdown(4000);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

你可能感兴趣的:(java,并发编程)