Java并发-线程的创建和终止

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangdong2012/article/details/79812041

线程是操作系统的最小执行单元,每个线程都归属于一个进程,一个进程往往是一个独立的服务。

创建线程

创建线程的过程分两步:声明任务和启动线程。声明任务是定义线程需要执行的具体内容,启动线程是将任务托管到线程上交给操作系统去执行。

声明任务

声明任务的方式有三种,分别是:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

下面分别看一下这三种方式的使用:

  • 继承Thread类
public static class ThreadTask extends Thread {
        //覆盖run方法,在此处编写业务逻辑
        @Override
        public void run() {
            System.out.println("Hello, I'am ThreadTask");
        }
}
public static void main(String[] args) {
        //创建线程实例
        Thread thread = new ThreadTask();
        //启动线程
        thread.start();
}

这种方式虽然能够实现预期效果,但并不推荐通过继承Thread来定义一个线程,因为这样会把任务到语义耦合到线程上。

  • 实现Runnable接口
public static class RunnableTask implements Runnable {
        //覆盖run方法,在此处编写业务逻辑
        @Override
        public void run() {
            System.out.println("Hello, I'am RunnableTask");
        }
 }

 public static void main(String[] args) {
        //声明任务
       Runnable task = new RunnableTask();
       //声明线程
       Thread thread = new Thread(task);
       thread.start();
}

与继承Thread相比,这种方式可以将要执行到业务逻辑和线程解耦合,线程只描述一个具体到执行单元,Runnable定义任务到具体内容。

  • 实现Callable接口
    Callable接口与Runnable接口具有相同的功能,区别在于Callable接口定义的任务可以获取其返回值,同时,提交Callable任务需要通过Java 5提高的线程调度框架ExecutorService,而不是用Thread.start()来启动线程。
public static class CallableTask implements Callable<String> {

        @Override
        public String call() throws Exception {
            return "Hello, I'am CallableTask";
        }
    }
    public static void main(String[] args) throws Exception{
    //声明一个线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //创建Callabletask实例
        Callable<String> callableTask = new CallableTask();
        //提交任务并等待返回值
        Future<String> result =  executorService.submit(callableTask);
        //打印返回值内容
        System.out.println(result.get());
    }

终止线程

终止线程的前提是线程一直在运行,那么怎么安全的通知线程终止运行呢,首先想到的是判断flag标记

     public static class RunnableTask1 implements Runnable {
        @Getter
        @Setter
        //volatile 保证变量的内存可见性,禁用指令重排序
        private volatile boolean canceled = false;
        @Override
        public void run() {
            try {
                while (!canceled) {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + " is running");
                }
                System.out.println(Thread.currentThread().getName() + " stoped, flag:" + canceled);
            }catch (Exception ex) {

            }
        }
    }

    public static void main(String[] args) throws Exception {
        RunnableTask1 runnable = new RunnableTask1();
        Thread t = new Thread(runnable);
        t.start();
        Thread t2 = new Thread(runnable);
        t2.start();
        TimeUnit.SECONDS.sleep(3);
        runnable.canceled = true;
    }
result:
Thread-1 is running
Thread-0 is running
Thread-0 is running
Thread-1 is running
Thread-0 is running
Thread-1 is running
Thread-0 stoped, flag:true
Thread-1 stoped, flag:true

Java给开发者提供来一种jvm层面的,更安全的线程interrupt策略,几乎所有的线程中断方式最终都依赖于线程中断,那么就来看一下Java中都线程中断。

线程中断

将到线程中断,就不得不看一下与线程中断机密关联到三个方法

new Thread().interrupted()
Thread.interrupted()
new Thread().isInterrupted()

线程中断就相当于上面提到到一个标记,只不过这个标记是��️Java提供的,下面通过一个例子来了解一下上面三个方法的作用和区别

private static class InterruptedTask implements Runnable {

        public volatile boolean canceled = false;
        @Override
        public void run() {
            try{
                while (!canceled) {
                    //该方法会抛出InterruptedException
                    //TimeUnit.SECONDS.sleep(1);
                    System.out.println("Task is running...");
                }
                //System.out.println("Task is end");
            }catch (Exception ex) {
                ex.printStackTrace();
                //System.out.println("ex flag:" + Thread.interrupted());
            }
        }
    }
   public static void main(String[] args) throws Exception{
        InterruptedTask task = new InterruptedTask();
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("main flag1:" + thread.isInterrupted());  //false
        TimeUnit.MILLISECONDS.sleep(1);
        //设置中断状态
        thread.interrupt();
        task.canceled = true;
        System.out.println("main flag2:" + thread.isInterrupted());  //true
    }

从上面的例子中可知:

  1. new Thread().isInterrupted() 方法用于获取当前线程的中断状态
  2. new Thread().interrupted() 方法用于设置当前线程的中断状态,即中断当前线程

下面有意思的来了,如果将

while (!canceled) {
       //该方法会抛出InterruptedException
       //TimeUnit.SECONDS.sleep(1);
       System.out.println("Task is running...");
}

中的TimeUnit.SECONDS.sleep(1);放开,那么得到的结果就是

main flag2:false
main flag2:false

这是因为,当线程中抛出Interrupted异常时,jvm会自动重置中断状态为false,至于为什么这么做,我想是为了提供更加安全的现在中断策略。

下面来演示一下Thread.interrupted()。

 private static class InterruptedTask implements Runnable {

        //public volatile boolean canceled = false;
        @Override
        public void run() {
            try{
                while (!Thread.interrupted()) {
                    //该方法会抛出InterruptedException
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("Task is running...");
                }
                System.out.println("Task is end");
                //throw new InterruptedException();
            }catch (Exception ex) {
                ex.printStackTrace();
                //System.out.println("ex flag:" + Thread.interrupted());
            }
        }
    }

    public static void main(String[] args) throws Exception{
        InterruptedTask task = new InterruptedTask();
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("main flag1:" + thread.isInterrupted());  //false
        TimeUnit.MILLISECONDS.sleep(1);
        //设置中断状态
        thread.interrupt();
        //task.canceled = true;
        System.out.println("main flag2:" + thread.isInterrupted());  //false
    }

此时的输出结果为:

main flag1:false
main flag2:false

因为线程最终终止运行了,就说明Thread.interrupted()在某个时刻的返回值是ture,那么为什么最后获取中断状态时是false呢? 来看一下该方法的说明:

Tests whether the current thread has been interrupted.  The
     * <i>interrupted status</i> of the thread is cleared by this method.  In
     * other words, if this method were to be called twice in succession, the
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).

其中有一句话是:

The interrupted status of the thread is cleared by this method

也就是调用该方法被调用时,会清除线程的中断状态,即使在该线程上调用了interrupt()方法,线程最终的中断状态还是false,因为被interrupt()方法重置了。

总结一下:

  • interrupt()方法用于设置现在中断状态为true
  • isInterrupted()方法用于获取线程线程的中断状态
  • Thread.interrupted()用于获取当前线程的中断状态,同时还会重置中断状态
  • 当线程中抛出InterruptedException时,线程的中断状态会被jvm
展开阅读全文

没有更多推荐了,返回首页