本系列参考资料《JAVA多线程编程核心技术》
关注公众号:雨中散步撒哈拉,回复:025
进行下载资料

[toc]

前言
上一篇已知有四种实现方式:

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
  3. 通过Callable和FutureTask创建线程
  4. 通过线程池创建线程

一、Thread方式实现

从jdk源码可以发现,Thread类实现了Runnable接口,他们之间是多态关系。其实,使用继承Thread类的方式创建新线程时,最大的局限就是不支持多继承,因为java语言的特性就是单根继承,所以为了多继承,完全可以实现Runnable接口的方式,一边实现一边继承。但是这两种方式创建的线程在工作时的性质是一样的,没有本质区别。

1. 探寻Thread实现方式

继承Thread类

  1. 定义一个类继承Thread类。
  2. 覆盖Thread类中的run方法。(方法run称为线程体)
  3. 直接创建Thread类的子类对象创建线程。
  4. 调用start方法,开启线程并调用线程的任务run方法执行。
    注意:run()方法和start()方法的区别。start()方法来启动线程,run()方法当作普通方法的方式调用,程序还是顺序执行。

1. 编写MyThread类

package com.multithreading.demo;

public class MyThread extends Thread {

    @Override
    public void run() {
        super.run();
        System.out.println("MyThread");
    }
}

2. 编写调用类

package com.multithreading.demo;

public class DemoTest {


    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        System.out.println("运行结束");
    }
}

3. 结果分析

image.png

从图中可以看出运行结束先于run方法结束,说明代码的运行结果与执行顺序或调用顺序是无关的!

2. 继续深入探寻Thread

上边已经发现线程调用具有随机性,那么当调用完成后,线程执行的顺序是按调用的顺序执行的吗?来,紧跟报道~~

为了实现效果,采用随机数休眠的形式,来使线程挂起的效果,从而模拟CPU执行!!

1. 编写线程内部代码

package com.multithreading.demo;

public class MyThreadSleep extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                int random = (int) (Math.random() * 1000);
                Thread.sleep(random);
                System.out.println("run=" + Thread.currentThread().getName());
            }
        }catch (InterruptedException interruptedException){
            interruptedException.printStackTrace();
        }
    }
}

2. 编写调用代码

package com.multithreading.demo;

public class DemoTestSleep {


    public static void main(String[] args) {
        MyThreadSleep thread = new MyThreadSleep();
        thread.setName("myThread");
        thread.start();
        try {
            for (int i = 0; i < 10; i++) {
                int random = (int) (Math.random() * 1000);
                Thread.sleep(random);
                System.out.println("run=" + Thread.currentThread().getName());
            }
        }catch (InterruptedException interruptedException){
            interruptedException.printStackTrace();
        }
    }
}

3. 执行并查看结果

image.png

发现main线程和mythread线程,执行的也是随机形式。所以对应刚开始猜想的。线程调用是随机,那么线程执行也是随机的吗?答案如上所述:线程执行是随机的

二、Ranble方式实现

Thread为继承方式,在java中具有很大的限制,因为java是单继承的!而使用Runnabe的形式可以避免这个问题,因为java多态是java扩展的重要特征!!!

其实查看Thread类,发现Thread也是实现Runnable实现的!
image.png

1. 编写run代码

package com.multithreading.demo;

public class MyRunnable implements Runnable {
    public void run() {
        System.out.println("MyRunnable");
    }
}

2. 编写调用代码

package com.multithreading.demo;

public class MyRunnableTest{

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println("运行结束");
    }
}

3. 调用执行

image.png

测试效果和Thread是一样的!因为Thread是实现Runnable而自定义的类直接实现Runnable其实质是一样的。

三、Callable和FutureTask方式实现

  1. 创建Callable接口的实现类 ,并实现Call方法
  2. 创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
  3. 使用FutureTask对象作为Thread对象的target创建并启动线程
  4. 调用FutureTask对象的get()来获取子线程执行结束的返回值

1. 编写run代码

package com.multithreading.demo;

import java.util.concurrent.Callable;

public class MyCallable<Object> implements Callable<Object> {

    public Object call() throws Exception {
        System.out.println("call = " + Thread.currentThread().getName());
        return null;
    }

}

2. 编写调用代码

package com.multithreading.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyCallableTest{

    public static void main(String[] args) {
        Callable<Object> myCallable = new MyCallable<Object>();
        FutureTask<Object> futureTask = new FutureTask<Object>(myCallable);

        Thread thread = new Thread(futureTask);
        thread.setName("myCallThread");
        thread.start();
        System.out.println("运行结束");
    }

}

3. 调用执行

image.png

四、线程池方式实现

ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值就很方便了。
通过分析可以知道,他同样也是实现了Callable接口,实现了Call方法,所以有返回值。这也就是正好符合了前面所说的两种分类

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。

再介绍Executors类:提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
image.png

  1. public static ExecutorService newFixedThreadPool(int nThreads)
    创建固定数目线程的线程池。
  2. public static ExecutorService newCachedThreadPool()
    创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
  3. public static ExecutorService newSingleThreadExecutor()
    创建一个单线程化的Executor。
  4. public static ScheduledExecutorService newScheduledThreadPool(int
    corePoolSize)
    创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
  5. ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

1. 编写run代码

package com.multithreading.demo;

public class MyExecutor implements Runnable{

    public void run() {
        System.out.println("executor = " + Thread.currentThread().getName());
    }
}

2. 编写调用代码

package com.multithreading.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyExecutorTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++){
            MyExecutor myExecutor = new MyExecutor();

            executorService.execute(myExecutor);
        }
        // 关闭线程池
        executorService.shutdown();
    }

}

3. 调用执行

image.png

线程池大小设置为5,执行十次,发现每个线程执行俩次!

Q.E.D.


只有创造,才是真正的享受,只有拚搏,才是充实的生活。