本博客系列是学习并发编程过程中的记录总结. 由于文章比较多, 写的时间也比较散, 所以我整理了个目录贴 (传送门), 方便查阅.
并发编程系列博客传送门
在 Java 中有多种方式可以实现多线程编程 (记得这是一道常问的面试题, 特别是在应届生找工作的时候被问的频率就更高了).
继承 Thread 类并重写 run 方法;
实现 Runnable 接口, 并将这个类的实例当做一个 target 构造 Thread 类
实现 Callable 接口;
继承 Thread 类
通过继承 Thread 类来实现多线程编程很容易. 下面代码中 MyThread 类继承了 Thread 类, 并重写了 run 方法.
但是这种方式不是很建议使用, 其中最主要的一个原因就是 Java 是单继承模式, MyThread 类继承了 Thread 类之后就不能再继承其他类了. 所以使用 implement 的形式比继承的方式更好. 线面会讲到使用 Runnable 接口实现多线程.
- public class MyThread extends Thread {
- public static final int THREAD_COUNT = 5;
- public static void main(String[] args) {
- List<Thread> threadList = new ArrayList<>();
- for (int i = 0; i <THREAD_COUNT; i++) {
- Thread thread = new MyThread();
- thread.setName("myThread--"+i);
- threadList.add(thread);
- }
- threadList.forEach(var->{var.start();});
- }
- @Override
- public void run() {
- super.run();
- System.out.println("my thread name is:"+Thread.currentThread().getName());
- Random random = new Random();
- int sleepTime = random.nextInt(5);
- try {
- TimeUnit.SECONDS.sleep(sleepTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- System.out.println(Thread.currentThread().getName()+"end after"+sleepTime+"seconds");
- }
- }
- }
实现 Runnable 接口实现多线程
下面我们就通过实现 Runnable 接口的形式来改造下上面的代码.
可以发现, 通过实现 Runnable 接口实现多线程编程也非常方便. 但是不需要再继承 Thread 类, 减少了耦合. 同时 new 了一个 Runner 对象后, 这个对象可以比较方便地在各个线程之间共享. 因此相对于继承 Thread 的方式, 更加推荐使用 Runnable 接口的方式实现多线程编程.
- public class MyThread {
- public static final int THREAD_COUNT = 5;
- public static void main(String[] args) {
- List<Thread> threadList = new ArrayList<>();
- Runner runner = new Runner();
- for (int i = 0; i <THREAD_COUNT; i++) {
- Thread thread = new Thread(runner);
- thread.setName("myThread--"+i);
- threadList.add(thread);
- }
- threadList.forEach(var->{var.start();});
- }
- public static class Runner implements Runnable{
- @Override
- public void run() {
- System.out.println("my thread name is:"+Thread.currentThread().getName());
- Random random = new Random();
- int sleepTime = random.nextInt(5);
- try {
- TimeUnit.SECONDS.sleep(sleepTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- System.out.println(Thread.currentThread().getName()+"end after"+sleepTime+"seconds");
- }
- }
- }
- }
实现 Callable 接口
上面介绍了两种方式都可以很方便地实现多线程编程. 但是这两种方式也有几个很明显的缺陷:
没有返回值: 如果想要获取某个执行结果, 需要通过共享变量等方式, 需要做更多的处理.
无法抛出异常: 不能声明式的抛出异常, 增加了某些情况下的程序开发复杂度.
无法手动取消线程: 只能等待线程执行完毕或达到某种结束条件, 无法直接取消线程任务.
为了解决以上的问题, 在 JDK5 版本的 java.util.concurretn 包中, 引入了新的线程实现机制: Callable 接口.
- @FunctionalInterface
- public interface Callable<V> {
- /**
- * Computes a result, or throws an exception if unable to do so.
- *
- * @return computed result
- * @throws Exception if unable to compute a result
- */
- V call() throws Exception;
- }
看了 Callable 接口的介绍, 其实这个接口的功能是和 Runnable 一样的, 和 Runnable 接口最主要区别就是:
Callable 接口可以有返回值;
Callable 接口可以抛出异常;
下面通过使用 Callable 接口的方式来改造下上面的代码:
- public class MyThread {
- public static final int THREAD_COUNT = 5;
- public static void main(String[] args) throws Exception {
- ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
- Runner runner = new Runner();
- for (int i = 0; i <THREAD_COUNT; i++) {
- Future<Integer> submit = executorService.submit(runner);
- //get 方法会一直阻塞等到线程执行结束
- System.out.println(submit.get());
- }
- executorService.shutdown();
- }
- public static class Runner implements Callable<Integer> {
- @Override
- public Integer call() throws Exception {
- System.out.println("my thread name is:"+Thread.currentThread().getName());
- Random random = new Random();
- int sleepTime = random.nextInt(500);
- try {
- TimeUnit.SECONDS.sleep(sleepTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- System.out.println(Thread.currentThread().getName()+"end after"+sleepTime+"seconds");
- }
- return sleepTime;
- }
- }
- }
上面代码中, 我们使用 Future 类来获取返回结果. Future 接口的主要方法如下:
isDone(): 判断任务是否完成.
isCancelled(): 判断任务是否取消.
get(): 获取计算结果 (一致等待, 直至得到结果).
cancel(true): 取消任务.
get(long,TimeUnit): 规定时间内获取计算结果 (在 long 时间内等待结果, 如果得到则返回; 如果未得到, 则结束, 并抛出 TimeoutException 异常).
来源: https://www.cnblogs.com/54chensongxia/p/11970827.html