今天简单说一下 Java 三种多线程实现方式和区别, 主要有实现 Runnable,Callable 和继承 Thread 三种方式.
实现 Runnable 的方式
这种方式比较常用, 当我们的线程类有继承其他的类的情况下(Java 不支持类多继承), 并且线程任务不需要返回值的情况下可以选用这种方式.
- public class ThreadRunnableDemo implements Runnable{
- /** 计数变量 */
- private int count = 0;
- public static void main(String[] args) throws InterruptedException {
- ThreadRunnableDemo threadRunnableDemo = new ThreadRunnableDemo();
- // 实例化线程
- Thread thread = new Thread(threadRunnableDemo, "threadRunnableDemoA");
- System.out.println(String.format("线程状态 preStart: %s", thread.getState()));
- // 启动线程
- thread.start();
- System.out.println(String.format("线程状态 afterStart: %s", thread.getState()));
- // 主线程休眠 1000ms
- Thread.sleep(1000);
- System.out.println(String.format("线程状态 after1000ms: %s", thread.getState()));
- }
- @Override
- public void run() {
- count++;
- System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
- Thread.currentThread().getName(), Thread.currentThread().getState(), count));
- }
- }
输出结果:
线程状态 preStart: NEW
线程状态 afterStart: RUNNABLE
线程名称: threadRunnableDemoA, 线程状态: RUNNABLE, count:1
线程状态 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 还有个好处就是可以线程可以抛异常, 如果我们需要在线程里抛出异常的话也可以选用这种方式, 其他两种方式只能捕获异常信息.
- public class ThreadCallableDemo implements Callable<Integer>{
- /** 计数变量 */
- private int count = 0;
- public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
- ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
- // 通过 FutureTask 获取返回值
- FutureTask<Integer> taskA = new FutureTask<>(threadCallableDemo);
- // 实例化线程
- Thread thread = new Thread(taskA, "threadCallableDemoA");
- System.out.println(String.format("线程状态 preStart: %s", thread.getState()));
- // 启动线程
- thread.start();
- System.out.println(String.format("线程状态 afterStart: %s", thread.getState()));
- // 通过 FutureTask 的 get()方法获取返回值
- int result = taskA.get();
- System.out.println("是否同步测试....");
- System.out.println(String.format("result: %s", result));
- System.out.println(String.format("线程状态 afterGetResult1: %s", thread.getState()));
- // 通过 FutureTask 的 get()方法获取返回值 设置超时时间 单位为 ms
- int resultWithTime = taskA.get(100, TimeUnit.MILLISECONDS);
- System.out.println(String.format("resultWithTime: %s", resultWithTime));
- System.out.println(String.format("线程状态 afterGetResult2: %s", thread.getState()));
- }
- /**
- * 实现 Callable 的 call 类
- */
- @Override
- public Integer call() throws Exception {
- // 自增
- count++;
- System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
- Thread.currentThread().getName(), Thread.currentThread().getState(), count));
- System.out.println("休眠 1000ms....");
- Thread.currentThread().sleep(1000);
- return count;
- }
- }
输出结果:
线程状态 preStart: NEW
线程状态 afterStart: RUNNABLE
线程名称: threadCallableDemoA, 线程状态: RUNNABLE, count:1
休眠 1000ms....
是否同步测试....
result: 1
线程状态 afterGetResult1: TERMINATED
resultWithTime: 1
线程状态 afterGetResult2: TERMINATED
继承 Thread 的方式
Thread 类实际上也是实现 Runnable 接口, 所以当我们继承 Thread 的时候我们即使不实现 run()方法也不会报错, 这种方式也经常用.
下面我写了两种不同继承 Thread 的代码, 大家可以看一下区别, 我在网上看到很多人说 继承 Thread 实现多线程, 线程间不能共享数据, 但是我用下面的代码 1 方式似乎也可以共享哇, 欢迎大家提出质疑.
代码 1:
- public class ThreadThreadDemo extends Thread{
- /** 计数变量 */
- private int count = 0;
- public static void main(String[] args) throws InterruptedException {
- ThreadThreadDemo threadThreadDemo = new ThreadThreadDemo();
- // 实例化线程
- Thread thread = new Thread(threadThreadDemo, "threadThreadDemoA");
- System.out.println(String.format("线程状态 preStart: %s", thread.getState()));
- // 启动线程
- thread.start();
- System.out.println(String.format("线程状态 afterStart: %s", thread.getState()));
- // 主线程休眠 1000s
- Thread.sleep(1000);
- System.out.println(String.format("线程状态 after1000ms: %s", thread.getState()));
- }
- @Override
- public void run() {
- count++;
- System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
- Thread.currentThread().getName(), Thread.currentThread().getState(), count));
- }
- }
输出结果 1:
线程状态 preStart: NEW
线程状态 afterStart: RUNNABLE
线程名称: threadThreadDemoA, 线程状态: RUNNABLE, count:1
线程状态 after1000ms: TERMINATED
代码 2:
- public class ThreadThreadDemo extends Thread{
- /** 计数变量 */
- private int count = 0;
- public static void main(String[] args) throws InterruptedException {
- ThreadThreadDemo threadThreadDemo = new ThreadThreadDemo();
- // 实例化线程
- System.out.println(String.format("线程状态 preStart: %s", threadThreadDemo.getState()));
- // 启动线程
- threadThreadDemo.start();
- System.out.println(String.format("线程状态 afterStart: %s", threadThreadDemo.getState()));
- // 主线程休眠 1000s
- Thread.sleep(1000);
- System.out.println(String.format("线程状态 after1000ms: %s", threadThreadDemo.getState()));
- }
- @Override
- public void run() {
- count++;
- System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
- Thread.currentThread().getName(), Thread.currentThread().getState(), count));
- }
- }
输出结果 2:
线程状态 preStart: NEW
线程状态 afterStart: RUNNABLE
线程名称: Thread-0, 线程状态: RUNNABLE, count:1
线程状态 after1000ms: TERMINATED
最后总结:
如果不要求线程返回结果, 也不需要抛异常也没有继承其他的类, 那么三种方式可以任选, 看喜好;
如果有继承其他类, 那么就只能用实现 Runnable 和实现 Callable 的方式;
如果需要线程返回结果或者需要线程抛异常那么选择实现 Callable 的方式的方式, 但是需要注意的是获取返回结果是同步的方式.
如果有疑问或者有问题欢迎留言讨论!
来源: https://www.cnblogs.com/sunshine6/p/12190127.html