Veiking百草园


JAVA多线程四种实现方式

程序员甲   @Veiking   2020-08-16

JAVA多线程四种实现方式

摘要:

java实现多线程有四种方式:一,通过继承Thread类;二,通过实现Runnable接口;三,通过Callable和FutureTask创建线程;四,通过线程池创建线程。其中继承Thread类和实现Runnable接口这两种方式无法返回结果,因为run方法的返回类型是void;如果我们想在任务执行完成后就可以获取执行结果,界的考虑使用Callable和Future构建的线程

Java多线程的实现

在java中实现多线程,有四种方式:
一,通过继承Thread类,重写run方法;
二,通过实现Runnable接口,重写run方法;
三,通过Callable和FutureTask创建线程
四,通过线程池创建线程

其中继承Thread类和实现Runnable接口这两种方式(Thread类本身也是Runnable的实现)无法返回结果,因为run方法的返回类型是void;如果我们想在任务执行完成后就可以获取执行结果,界的考虑使用Callable和Future构建的线程。

一,继承Thread类,重写run()方法,实现多线程


/**
 * @author :Veiking
 * @version    :
 * @version : 说明 :继承Thread类,实现多线程
 */
public class MyThreadTest {
    public static void main(String[] args) {
        for (int i = 0; i < 30; i++) {
            System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
            if (i % 5 == 0) {
                Thread myThreadA = new MyThread();     // 创建新的线程,线程进入新建状态
                Thread myThreadB = new MyThread();
                myThreadA.start();                     // 调用start()方法,线程进入就绪状态
                myThreadB.start();
            }
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + "\t " + i);
        }
    }
}

从代码中可以看到,通过继承Thread类,重写run() 方法定义了一个新的线程类MyThread,其中run() 方法的方法体填充的是线程具体的业务,可以称之为线程执行体

当这个 MyThread 类 new 出对象时,既可以创建一个新建状态的线程。然后通过调用这个对象的 start() 方法,这时候该线程就进入到就绪状态;就绪状态的线程并不一定会马上执行,具体取决于CPU的调度。

看源码我们可以了解到,Thread类其实也是实现了Runnable接口。

二,实现Runnable接口,重写run()方法,实现多线程

/**
* @author    :Veiking
* @version    :
* 说明        :实现Runnable接口,重写run()方法,实现多线程
*/
public class MyRunnableTest {

    public static void main(String[] args) {
        for (int i = 0; i < 30; i++) {
            System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
            if (i % 5 == 0) {
                Runnable myRunnable = new MyRunnable();     // 创建一个Runnable实现类的对象
                Thread myThreadA = new Thread(myRunnable);    // 将myRunnable作为Thread target创建新的线程
                Thread myThreadB = new Thread(myRunnable);
                myThreadA.start();                             // 调用start()方法,线程进入就绪状态
                myThreadB.start();
            }
        }
    }

}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + "\t " + i);
        }
    }

}

上面的代码,我们创建了Runnable实现类的实例对象,并以此对象作为Thread类的target,然后创建Thread对象,这个Thread对象才是真正的线程对象

三,通过使用Callable接口和FutureTask类,实现多线程

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

/**
* @author    :Veiking
* @version    :
* 说明        :通过使用Callable和Future接口,实现多线程
*/
public class MyCallableTest {

    public static void main(String[] args) {

        Callable myCallable = new MyCallable();                        // 创建 MyCallable 实例对象
        FutureTask futureTask = new FutureTask(myCallable);     // 将 myCallable 对象作为参数传递给 FutureTask 进行初始化
        for (int i = 0; i < 15; i++) {
            System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
            if (i == 2) {
                System.out.println("New Thread ...");
                Thread thread = new Thread(futureTask);       // 用 futureTask 对象作为 Thread 对象的 target 创建新的线程
                thread.start();                              // 调用start()方法,线程进入就绪状态
            }
        }

        System.out.println("MainThread for end ...");
        try {
            int sum = futureTask.get();            // 调用 futureTask 对象的 get() 方法获取子线程执行的返回值
            System.out.println("myCallable thread sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

/**
 * @author    :Veiking
 * 说明        :创建Callable接口的实现类 ,并实现Call方法
 *
 */
class MyCallable implements Callable {
     // 与run()方法不同的是,call()方法具有返回值
     @Override
     public Integer call()  {
        int sum = 0;
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + "\t " + i);
            sum += i;
        }
        return sum;
    }
}

我们看看这段代码运行的结果:

根据代码我们可以看到,通过使用Callable和FutureTask接口,创建多线程,一般分为以下几个步骤:
1,创建Callable接口的实现类 ,并实现Call方法
2,创建 MyCallable 实例对象,然后将 myCallable 对象作为参数传递给 FutureTask 进行初始化
3,用 futureTask 对象作为 Thread 对象的 target 创建新的线程
4,调用 futureTask 对象的 get() 方法获取子线程执行的返回值

这里,根据源码了解到,FutureTask类实现了RunnableFuture接口,而RunnableFuture接口继承的是Runnable和Future这两个接口,也就是说线程方面还是通过Runnable实现的。我们又看了看FutureTask的 get() 方法,其本质是一个阻塞式的监听,通过判断当前task的状态为COMPLETING 且没有其他异常的情况下,返回获取值。

四,通过线程池,实现多线程

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

/**
* @author    :Veiking
* @version    :
* 说明        :通过线程池,实现多线程
*/
public class MyExecuteTest {
   private static final int POOL_SIZE = 5; // 线程大小
   private static final int POOL_NUM = 20; // 要启动的线程数量 

   public static void main(String[] args) {
        // 初始线程池
        ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
        for(int i = 0; i < POOL_NUM; i++) {
           MyExecuteRunnable thread = new MyExecuteRunnable();
            executorService.execute(thread);
        }
        //关闭线程池
        executorService.shutdown();
    }
}

class MyExecuteRunnable implements Runnable {
   @Override
   public void run() {
      System.out.println("Thread By ThreadPool :" + Thread.currentThread().getName() + " ");
   }
}

从代码可以看出,我们创建了一个容量为5的线程池,然后设置了20个线程,置入线程池去执行。这时候线程池里的总线程数量不会超过5,这里边其实是一个线程队列,执行完一个线程,然后再去队列里取一个执行。

很明显,这种线程池机制可以避免开启过多线程,导致资源的占用率过高,影响整体的性能。

查看源码了解到,Executors类使用了工厂模式的方法来创建线程池,这些返回的线程池都实现了ExecutorService接口,如图:

我们平时常用到的其实也就四种:
newFixedThreadPool(int nThreads); // 创建固定数目线程的线程池
newCachedThreadPool(); // 创建一个可缓存的线程池,即使用之前会判断下有没有可用线程缓存,空闲时也会一处超时过期的缓存线程
newSingleThreadExecutor(); // 创建一个单线程化的Executor
newScheduledThreadPool(int corePoolSize); // 创建一个支持定时任务的线程池,也可用来替代Timer类

* 额外补充,线程池使用使用Callable

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* @author    :Veiking
* @version    :
* 说明        :通过线程池使用Callable和Future的方式
*/
public class MyCallableExecuteTest {
    private static final int POOL_SIZE = 5; // 线程大小
    private static final int POOL_NUM = 20; // 要启动的线程数量 

    public static void main(String[] args) {
        // 初始线程池
        ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
        Future future = executorService.submit(new MyExecuteCallable());
        for(int i = 0; i {
     // 与run()方法不同的是,call()方法具有返回值
     @Override
     public Integer call()  {
        int sum = 0;
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + "\t " + i);
            sum += i;
        }
        return sum;
    }
}

看源码的时候又看到这个submit() 方法了,就顺手试了下;通过submit()方法,可以传递一个Callable,返回Future对象,然后我们同样可以通过Future对象的get()方法,获取Callable实例中call()方法返回的值

好了上面就是在 java 代码中实现多线程的四种方式。


程序员甲


潜影拾光

老子坐清源

天地不仁,以万物为刍狗。

扫码转发

二维码
二维码
二维码
二维码
二维码
二维码

博文标签