创建线程有几种方式?
    创建线程的方式其实只有一种 就是通过Thread类来创建的 只是传的参数不同
    对于实现Runable接口和实现Callable接口 是无法通过实现类来直接创建线程的
    通过实现类对象来调用对应的重写方法也是没有办法启动线程的 这个方法会被当做普通方法来使用
    只有通过Thread类中start方法才能启动一个线程

    什么情况下用多线程?

    如果想要在同一时间内 做更多的事情 是可以考虑使用多线程来提高效率的
    就比如看视频 视频的播放和声音的调节就用到了多线程
    不可能因为调节声音 而导致视频播放停止
    所以在某些场景下 合理使用多线程能帮助我们高效的处理事情

    串行更快还是并行更快?(是不是多线程效率就一定会很高)
    这个不确定 需要看每个线程需要消耗多少资源
    因为创建线程需要消耗资源 切换线程也需要消耗资源
    所以如果消耗的资源比较少那串行(单线程)可能会更快

    继承Thread和实现Runnable来构造Thread有什么区别?
    从结果上来讲 没有区别 一样的
    但是 java是单继承的 如果继承了Thead就不能继承别的了
    所以继承是很宝贵的 实现是很容易的

    Runable接口和Callable接口的区别
    Runable接口中是run方法 无返回值 不可以抛异常
    Callable接口中是call方法 有返回值 可以抛异常

    实现Callable接口创建线程的方式

    1. public static void main(String[] args) {
    2. MyCall myCall = new MyCall(100000000);
    3. FutureTask<Long> future = new FutureTask<Long>(myCall);
    4. //FutureTask类实现了Runable接口 所以能传入Thread中
    5. Thread thread = new Thread(future);
    6. thread.start();
    7. long a = future.get();
    8. //get方法可以获取返回值 但该方法会阻塞 直到线程执行完拿到结果
    9. System.out.println("A:" + a);
    10. }
    11. public class MyCall implements Callable<Long> {
    12. //返回值类型对应接口的泛型 如果不写默认是Object
    13. private int target;
    14. public MyCall(int target) {
    15. this.target = target;
    16. }
    17. //拓展
    18. //对于int类型的计算 返回结果要为long类型的
    19. //因为计算结果可能会超过int的范围 那么此次得到的结果就会有误
    20. @Override
    21. public Long call() throws Exception {
    22. long result = 0;
    23. for (int i = 0 ; i <= target ; i ++) {
    24. result += i;
    25. }
    26. return result;
    27. }
    28. }

    为什么实现了Callable方法就能拿到返回值了?

    我们在实现Callable接口后 不能直接通过实现类传递给Thread来创建线程
    还需要通过一个中间类进行转换 也就是FutureTask这个类
    先将Callable实现类传递给FutureTask来创建一个FutureTask对象

    然后再将FutureTask对象传递给Thread来创建线程
    而FutureTask这个类实现了Runnable接口 且重写了run方法
    所以Thread执行的是FutureTask中的run方法
    其中 FutureTask 的run方法中 执行了我们传入的Callable实现类的call方法
    然后将call方法的返回值存到 存在outcome对象里(Object类型的)等待被get
    get方法 如果outcome为空 就等待 如果不为空就将值返回
    所以如果调用了get方法会阻塞

    简单仿写一个FutureTask的代码

    1. import java.util.concurrent.Callable;
    2. public class MyFuture<T> implements Runnable{
    3. private Object outcome;
    4. private Callable<T> callable;
    5. public MyFuture(Callable<T> callable){
    6. this.callable = callable;
    7. }
    8. @Override
    9. public void run() {
    10. try {
    11. outcome = callable.call();
    12. } catch (Exception e) {
    13. e.printStackTrace();
    14. }
    15. }
    16. public T get(){
    17. while (true){
    18. if (outcome!=null){
    19. return (T)outcome;
    20. }
    21. try {
    22. Thread.sleep(100);
    23. } catch (InterruptedException e) {
    24. e.printStackTrace();
    25. }
    26. }
    27. }
    28. }

    FutureTask的细节

    1. public class TestMain{
    2. public static void main(String[] args) throws Exception {
    3. MyCall myCall = new MyCall(100000000);
    4. FutureTask<Long> futureA = new FutureTask<Long>(myCall);
    5. Thread threadA = new Thread(futureA);
    6. threadA.start();
    7. long a = futureA.get();
    8. //get方法会阻塞 直到线程执行完 拿到结果
    9. //所以此时根本没有达到多线程的效果
    10. //因为程序执行到这里后 get方法只能拿到返回值 后续代码才会执行
    11. System.out.println("A:" + a);
    12. FutureTask<Long> futureB = new FutureTask<Long>(myCall);
    13. Thread threadB = new Thread(futureB);
    14. threadB.start();
    15. long b = futureB.get();
    16. System.out.println("B:" + b);
    17. //所以要想实现多线程取值 那么需要将取值放在最后
    18. // long a = futureA.get();
    19. // System.out.println("A:" + a);
    20. // long b = futureB.get();
    21. // System.out.println("B:" + b);
    22. }
    23. }
    24. class MyCall implements Callable<Long> {
    25. //返回值类型对应接口的泛型 如果不写默认是Object
    26. private int target;
    27. public MyCall(int target) {
    28. this.target = target;
    29. }
    30. //拓展
    31. //对于int类型的计算 返回结果要为long类型的
    32. //因为计算结果可能会超过int的范围 那么此次得到的结果就会有误
    33. @Override
    34. public Long call() throws Exception {
    35. long result = 0;
    36. for (int i = 0 ; i <= target ; i ++) {
    37. result += i;
    38. }
    39. return result;
    40. }
    41. }