zoukankan      html  css  js  c++  java
  • Java多线程的三种实现方式

    今天简单说一下Java三种多线程实现方式和区别,主要有实现Runnable、Callable和继承Thread三种方式。

    实现Runnable的方式

    这种方式比较常用,当我们的线程类有继承其他的类的情况下(Java不支持类多继承),并且线程任务不需要返回值的情况下可以选用这种方式。

     1 public class ThreadRunnableDemo implements Runnable{
     2 
     3     /** 计数变量 */
     4     private int count = 0;
     5     
     6     public static void main(String[] args) throws InterruptedException {
     7         
     8         ThreadRunnableDemo threadRunnableDemo = new ThreadRunnableDemo();
     9         
    10         //实例化线程
    11         Thread thread = new Thread(threadRunnableDemo, "threadRunnableDemoA");
    12         System.out.println(String.format("线程状态preStart: %s", thread.getState()));
    13         
    14         //启动线程
    15         thread.start();
    16         System.out.println(String.format("线程状态afterStart: %s", thread.getState()));
    17         
    18         //主线程休眠1000ms
    19         Thread.sleep(1000);
    20         System.out.println(String.format("线程状态after1000ms: %s", thread.getState()));
    21 
    22     }
    23 
    24     @Override
    25     public void run() {
    26         
    27         count++;
    28         
    29         System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s", 
    30                 Thread.currentThread().getName(), Thread.currentThread().getState(), count));
    31         
    32     }
    33 }

    输出结果:

    1 线程状态preStart: NEW
    2 线程状态afterStart: RUNNABLE
    3 线程名称:threadRunnableDemoA, 线程状态:RUNNABLE, count:1
    4 线程状态after1000ms: TERMINATED

    实现Callable的方式

    当我们执行线程需要返回值的时候那么就必须选用实现Callable类的方式,因为目前只有这种方式能返回值。当然这种方式我们也可以不需要获取返回值。

    这种方式是通过FutureTask的get()方法(下面代码的第22行)或者get(long timeout, TimeUnit unit)(下面代码的第28行)方法获取返回值。当我们看Callable的接口定义的源码会发现“public interface Callable<V> ” ,我们实现的时候是需要定义返回类型,如下面代码所示。

    除此之外我们还需要注意的是:当我们通过FutureTask的get()方法去获取线程的返回值的时候是要等到线程call()内容都执行完毕之后才能获取得到,并且get()方法后面的代码必须等待,说明这一定是同步的,所以我们可以在真正需要线程返回值的时候才通过get()方法去获取,以免被阻塞。当我们通过get(long timeout, TimeUnit unit)方式去获取的时候可以设置超时时间,如果超过所设置的超时时间都没有获取到线程返回的值则会抛出 java.util.concurrent.TimeoutException 异常,当然如果在get(long timeout, TimeUnit unit)之前用get()方式获取了的话就不会抛异常。

    实现Callable还有个好处就是可以线程可以抛异常,如果我们需要在线程里抛出异常的话也可以选用这种方式,其他两种方式只能捕获异常信息。

     1 public class ThreadCallableDemo implements Callable<Integer>{
     2     
     3     /** 计数变量 */
     4     private int count = 0;
     5 
     6     public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
     7         
     8         ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
     9         
    10         //通过FutureTask获取返回值
    11         FutureTask<Integer> taskA = new FutureTask<>(threadCallableDemo);
    12         
    13         //实例化线程
    14         Thread thread = new Thread(taskA, "threadCallableDemoA");
    15         System.out.println(String.format("线程状态preStart: %s", thread.getState()));
    16         
    17         //启动线程
    18         thread.start();
    19         System.out.println(String.format("线程状态afterStart: %s", thread.getState()));
    20         
    21         //通过FutureTask的get()方法获取返回值
    22         int result = taskA.get();
    23         System.out.println("是否同步测试....");
    24         System.out.println(String.format("result: %s", result));
    25         System.out.println(String.format("线程状态afterGetResult1: %s", thread.getState()));
    26         
    27         //通过FutureTask的get()方法获取返回值 设置超时时间 单位为ms
    28         int resultWithTime = taskA.get(100, TimeUnit.MILLISECONDS);
    29         System.out.println(String.format("resultWithTime: %s", resultWithTime));
    30         System.out.println(String.format("线程状态afterGetResult2: %s", thread.getState()));
    31         
    32     }
    33 
    34     /**
    35      * 实现Callable的call类
    36      */
    37     @Override
    38     public Integer call() throws Exception {
    39         
    40         //自增
    41         count++;
    42         
    43         System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s", 
    44                     Thread.currentThread().getName(), Thread.currentThread().getState(), count));
    45         System.out.println("休眠1000ms....");
    46         Thread.currentThread().sleep(1000);
    47         return count;
    48     }
    49 }

    输出结果:

    1 线程状态preStart: NEW
    2 线程状态afterStart: RUNNABLE
    3 线程名称:threadCallableDemoA, 线程状态:RUNNABLE, count:1
    4 休眠1000ms....
    5 是否同步测试....
    6 result: 1
    7 线程状态afterGetResult1: TERMINATED
    8 resultWithTime: 1
    9 线程状态afterGetResult2: TERMINATED

    继承Thread的方式 

    Thread类实际上也是实现Runnable接口,所以当我们继承Thread的时候我们即使不实现run()方法也不会报错,这种方式也经常用。

    下面我写了两种不同继承Thread的代码,大家可以看一下区别,我在网上看到很多人说 继承Thread实现多线程,线程间不能共享数据,但是我用下面的代码1方式似乎也可以共享哇,欢迎大家提出质疑。

    代码1:

     1 public class ThreadThreadDemo extends Thread{
     2 
     3     /** 计数变量 */
     4     private int count = 0;
     5     
     6     public static void main(String[] args) throws InterruptedException {
     7         
     8         ThreadThreadDemo  threadThreadDemo = new ThreadThreadDemo();
     9         
    10         //实例化线程
    11         Thread thread = new Thread(threadThreadDemo, "threadThreadDemoA");
    12         System.out.println(String.format("线程状态preStart: %s", thread.getState()));
    13         
    14         //启动线程
    15         thread.start();
    16         System.out.println(String.format("线程状态afterStart: %s", thread.getState()));
    17         
    18         //主线程休眠1000s
    19         Thread.sleep(1000);
    20         System.out.println(String.format("线程状态after1000ms: %s", thread.getState()));
    21     }
    22     
    23     @Override
    24     public void run() {
    25         
    26         count++;
    27         
    28         System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s", 
    29                 Thread.currentThread().getName(), Thread.currentThread().getState(), count));
    30     }
    31 }

    输出结果1:

    1 线程状态preStart: NEW
    2 线程状态afterStart: RUNNABLE
    3 线程名称:threadThreadDemoA, 线程状态:RUNNABLE, count:1
    4 线程状态after1000ms: TERMINATED

     代码2:

     1 public class ThreadThreadDemo extends Thread{
     2 
     3     /** 计数变量 */
     4     private int count = 0;
     5     
     6     public static void main(String[] args) throws InterruptedException {
     7         
     8         ThreadThreadDemo  threadThreadDemo = new ThreadThreadDemo();
     9         
    10         //实例化线程
    11         System.out.println(String.format("线程状态preStart: %s", threadThreadDemo.getState()));
    12         
    13         //启动线程
    14         threadThreadDemo.start();
    15         System.out.println(String.format("线程状态afterStart: %s", threadThreadDemo.getState()));
    16         
    17         //主线程休眠1000s
    18         Thread.sleep(1000);
    19         System.out.println(String.format("线程状态after1000ms: %s", threadThreadDemo.getState()));
    20     }
    21     
    22     @Override
    23     public void run() {
    24         
    25         count++;
    26         
    27         System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s", 
    28                 Thread.currentThread().getName(), Thread.currentThread().getState(), count));
    29     }
    30 }

    输出结果2:

    1 线程状态preStart: NEW
    2 线程状态afterStart: RUNNABLE
    3 线程名称:Thread-0, 线程状态:RUNNABLE, count:1
    4 线程状态after1000ms: TERMINATED

    最后总结:

    1. 如果不要求线程返回结果,也不需要抛异常也没有继承其他的类,那么三种方式可以任选,看喜好;
    2. 如果有继承其他类,那么就只能用实现Runnable和实现Callable的方式;
    3. 如果需要线程返回结果或者需要线程抛异常那么选择实现Callable的方式的方式,但是需要注意的是获取返回结果是同步的方式。

    如果有疑问或者有问题欢迎留言讨论!

    下面是我的个人公众号,欢迎关注,一起学习,可以方便看我的文章!

  • 相关阅读:
    Minimum Path Sum,最短路径问题,动态规划
    UniquePaths,UniquePaths2,路径问题。动态规划。
    LengthOfLastWord,字符串最后一个子串的长度
    间隔问题,合并间隔(merge interval),插入间隔(insert interval)
    矩阵螺旋遍历Spiral Matrix,Spiral Matrix2
    Centos 5.2 下配置 php 的 json 扩展
    一个睡五分钟等于六个钟头的方法
    js div 排除内部的点击事件 就是 冒泡的处理
    做微信开发 “人脉圈的” 总结
    YII 学习笔记
  • 原文地址:https://www.cnblogs.com/sunshine6/p/12190127.html
Copyright © 2011-2022 走看看