线程实现的方式
1.继承 Thread 类,Thread的底层用的也是Runnable接口
package com.opendev.Thread; import java.util.Map; public class MyThread extends Thread { /** * 继承Thread方式 * * * 继承Thread类,需要覆盖方法 run()方法, * 在创建Thread类的子类时需要重写 run(),加入线程所要执行的代即可 * * */ private String acceptStr; public MyThread(String acceptStr) { this.acceptStr = acceptStr; } @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("这个传给我的值:" + acceptStr + ",加上一个变量,看看是什么效果:" + i); } } public static void main(String[] args) { new MyThread("Thread测试").start(); new MyThread("Thread测试").start(); } }
2.实现 Runnable 接口
package com.opendev.Thread; public class MyRunable implements Runnable { /** * 实现Runnable方式 * <p> * <p> * <p> * Runnable是可以共享数据的,多个Thread可以同时加载一个Runnable, * 当各自Thread获得CPU时间片的时候开始运行Runnable,Runnable里面的资源是被共享的, * 所以使用Runnable更加的灵活。PS:需要解决共享之后产生的资源竞争问题 * <p> * <<资源共享,案例 抢火车票>> */ private String acceptStr; public MyRunable(String acceptStr) { this.acceptStr = acceptStr; } @Override public void run() { try { // 线程阻塞1秒,此时有异常产生,只能在方法内部消化,无法上抛 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 最终处理结果无法返回 System.out.println("hello : " + this.acceptStr); } public static void main(String[] args) { Runnable runnable = new MyRunable("Runable测试"); long beginTime = System.currentTimeMillis(); new Thread(runnable).start(); long endTime = System.currentTimeMillis(); // endTime 和 beginTime是一样的,线程并不会阻塞主线程 System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!"); } }
3.实现 Callable 接口,它具有返回值 call , 这个新特性从Java1.5之后才有
package com.opendev.Thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<String> { /** * 实现 Callable 接口的方法 * @return * @throws Exception * * 介绍: *如果你希望任务在完成的能返回一个值,那么可以实现Callable接口而不是Runnable接口。 *在Java SE5中引入的Callable是一种具有类型参数的泛型, *它的参数类型表示的是从方法call()(不是run())中返回的值。 * * */ private String acceptStr; public MyCallable(String acceptStr) { this.acceptStr = acceptStr; } @Override public String call() throws Exception { // 任务阻塞1秒,并且增加一些信息返回 Thread.sleep(1000); return this.acceptStr + " 增加一些字符并返回"; } public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<String> callable = new MyCallable("Callable测试"); FutureTask<String> task = new FutureTask<String>(callable); // 创建线程 new Thread(task).start(); long beginTime = System.currentTimeMillis(); // 调用get()阻塞主线程,反之,线程不会阻塞 String result = task.get(); long endTime = System.currentTimeMillis(); System.out.println("hello : " + result); // endTime 和 beginTime是不一样的,因为阻塞了主线程 System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!"); } }
总结:
Runnable、Thread、Callable总结
最后再来看看它们三个之间的总结。
实现Runnable接口相比继承Thread类有如下优势
1)可以避免由于Java的单继承特性而带来的局限
2)增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
3)适合多个相同程序代码的线程去处理同一资源的情况
4)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
实现Runnable接口和实现Callable接口的区别
1)Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
2)实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回结果
3)Callable接口的call()方法允许抛出异常,而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛
4)加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法
注:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞
有志者、事竟成,破釜沉舟,百二秦关终属楚;
苦心人、天不负,卧薪尝胆,三千越甲可吞吴.
加油吧,致每个正在奋斗路上的你!!!