线程实现的方式

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();
    }
}
View Code

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!");
    }
}
View Code

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!");
    }
}
View Code

总结:

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()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

原文地址:https://www.cnblogs.com/cb1186512739/p/12735925.html