[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)

文章目录

  • 概述
  • 一、创建&运行线程
    • 1、继承Thread类
    • 2、实现Runnable接口
    • 3、join()
  • 二、管理线程
      • Executor
      • ExecutorService
      • ScheduledExecutorService
      • 1、ExecutorService单线程
      • 2、ExecutorService多线程
      • 3、Thread Pool
      • 4、ScheduledExecutorService

概述

最近的工作中遇到了融合数据的场景,采取了多线程的方案解决了问题,今天就回顾一下JAVA的多线程编程。

一、创建&运行线程

1、继承Thread类

public class Application extends Thread{
    public Application() {
        super();
    }

    public Application(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println("application线程:" + Thread.currentThread().getName());
    }

    public static void main(String[] args){
        System.out.println("开始创建一个线程类");
        Application application = new Application("MyApplication");
        System.out.println("main线程:" + Thread.currentThread().getName());
        System.out.println("开始运行线程:");
        application.start();
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第1张图片

Thread.currentThread()返回当前执行中的线程的引用,Thread(String name)构造方法给线程命名。

2、实现Runnable接口

Runnable接口只定义了一个run()方法,Thread类也实现了Runnable接口

public class Application implements Runnable{

    @Override
    public void run() {
        System.out.println("application线程:" + Thread.currentThread().getName());
    }

    public static void main(String[] args){
        System.out.println("开始创建一个线程类");
        Application application = new Application();
        System.out.println("main线程:" + Thread.currentThread().getName());
        System.out.println("---创建线程---");
        Thread thread = new Thread(application);
        System.out.println("---开始运行线程---");
        thread.start();
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第2张图片
通过匿名类进行定义:

public class Application{
    public static void main(String[] args){
        System.out.println("main线程:" + Thread.currentThread().getName());
        System.out.println("---创建Runnable---");
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable线程:" + Thread.currentThread().getName());
            }
        };
        Thread thread = new Thread(runnable);
        System.out.println("---开始运行线程---");
        thread.start();
    }
}

通过Lamda表达式来定义,Lamda表达式作为Java 1.8的特性,大大简化了代码,其核心在于其对方法的推导,即对函数式编程的支持。

public class Application{
    public static void main(String[] args){
        System.out.println("main线程:" + Thread.currentThread().getName());
        System.out.println("---创建Runnable---");
        Thread thread = new Thread(
                () -> {
                    System.out.println("runnable线程:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("sleep了2秒");
                }
        );
        System.out.println("---开始运行线程---");
        thread.start();
    }
}

3、join()

thread1.join()调用时表示thread1的线程加入到当前线程中,join()后的代码需要等待thread1线程执行完毕才能执行,即join()阻塞当前线程。join()方法也可以设置等待时间,即阻塞时间, join(1000)表示阻塞1秒

public class Application {

    public static void main(String[] args) throws InterruptedException {
        // Create Thread 1
        Thread thread1 = new Thread(() -> {
            System.out.println("Entered Thread 1");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Exiting Thread 1");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Entered Thread 2");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Exiting Thread 2");
        });
        System.out.println("Starting Thread 1");
        long start = System.currentTimeMillis();
        thread1.start();
        System.out.println("Waiting for Thread 1 to complete");
        try {
            thread1.join();
            System.out.println(System.currentTimeMillis()-start);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        System.out.println("Starting Thread 2");
        thread2.start();
        thread2.join(1000);
        System.out.println(System.currentTimeMillis()-start);
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第3张图片

二、管理线程

Java并发包中主要定义提供了以下三个用于创建管理线程的接口:

Executor

一个仅包含了一个execute()方法的接口,用于启动Runnable对象指定的任务。

ExecutorService

Executor的子接口,增加了管理任务生命周期的功能,提供了submit()方法,其可以接受RunnableCallable对象,Callable对象和Runnable对象非常相似,只不过Callablecall()可以返回一个值。

ScheduledExecutorService

ExecutorService的子接口,增加了执行计划任务的功能。


除了以上接口,Executors类提供了创建不同Executor的工厂方法。

1、ExecutorService单线程

创建一个单线程的ExecutorService并提交任务来看一看执行效果:

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

public class Application {

    public static void main(String[] args) {
        System.out.println("main Thread: " + Thread.currentThread().getName());

        System.out.println("创建Executor...");
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        long start = System.currentTimeMillis();

        System.out.println("提交Runnable1:");
        executorService.submit(
                () -> {
                    System.out.println("Runnable1:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Runnable1执行完:" + (System.currentTimeMillis() - start));
                }
        );

        System.out.println("提交Runnable2:");
        executorService.submit(
                () -> {
                    System.out.println("Runnable2:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Runnable2执行完:" + (System.currentTimeMillis() - start));
                }
        );
        System.out.println("全部提交:" + (System.currentTimeMillis() - start));
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第4张图片

上面的代码使用Executors.newSingleThreadExecutor()创建了一个单线程的ExecutorService来执行任务,当提交一个任务后,如果ExecutorService中已有一个任务在执行,那么新提交的任务将会在队列中等待,直到前一个任务释放线程后,再去执行。可以看到上述的代码执行后并没有结束,那是因为ExecutorService还在保持监听新的任务提交,直到我们主动关闭它,有下列两个方法:

  • shutdown() 当这个方法被调用时,ExecutorService将会停止接受新的任务,等待之前被提交的任务都执行完毕后终止。
  • shutdownNow() 当这个方法被调用时,将会立刻停止接受新的任务提交,终止正在执行中的任务,并返回还未执行的任务列表。

2、ExecutorService多线程

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

public class Application {

    public static void main(String[] args) {
        System.out.println("main Thread: " + Thread.currentThread().getName());

        System.out.println("创建Executor...");
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        long start = System.currentTimeMillis();

        System.out.println("提交Runnable1:");
        executorService.submit(
                () -> {
                    System.out.println("Runnable1:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Runnable1执行完:" + (System.currentTimeMillis() - start));
                }
        );

        System.out.println("提交Runnable2:");
        executorService.submit(
                () -> {
                    System.out.println("Runnable2:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Runnable2执行完:" + (System.currentTimeMillis() - start));
                }
        );

        System.out.println("提交Runnable3:");
        executorService.submit(
                () -> {
                    System.out.println("Runnable3:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Runnable3执行完:" + (System.currentTimeMillis() - start));
                }
        );

        executorService.shutdown();
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第5张图片
可见利用了Executors.newFixedThreadPool(2)定义了一个包含2个线程的固定线程池,其保证了在有任务提交时,始终都能拿到线程去执行任务。当两个线程都被占用时,任务则会在队伍中等待。

3、Thread Pool

  • 大部分Executor的实现都是利用了线程池去执行任务,线程池本质为与RunnableCallable分开的Thread集合并且管理有管理Thread集合的能力,负责了Thread的创建和执行。
  • ThreadPool的优势在于只需要创建一次线程,就能长期维护并复用线程执行任务。Executors.newFixedThreadPool(2)方法创建的实质是ThreadPoolExecutor,其不光是维护ThreadPool,它还维护着一个任务队列。
  • 出于好奇心,我还是去看了一下源码,只说其队列,线程创建,线程集合部分的代码。因为其他部分我也没有学到,在之后的博客中讲吧。
    [JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第6张图片
    // 创建线程池,可见任务队列用的是LinkedBlockingQueue()
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

	// --------------------------------------------------------
    // 进入构造方法
    // 这里给到了创建线程的默认工厂Executors.defaultThreadFactory()
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

	// --------------------------------------------------------
	// 进入全参构造方法
	// 在这个构造方法中并没有看到线程被创建,显然是在submit()中创建了
	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
	 
	// --------------------------------------------------------
	// 进入submit,忽然发现ThreadPoolExecutor并没有重写父类AbstractExecutorService中的submit方法,于是直接去看父类
	// 进入AbstractExecutorService,看这个代码,貌似奥妙就全在这个execute方法里了,这个抽象类继承了Executor接口,但是并没有实现execute方法,看来又要回到ThreadPoolExecutor了
	public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

	// --------------------------------------------------------
	// 进入ThreadPoolExecutor中的execute
	// 这里的注释其实已经写得很明白,主要是对线程池和队列做检查,我们不管那么多,先瞅瞅addWorker()方法。
	public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

	// --------------------------------------------------------
	// 进入addwork()
	// 从截取的代码片段可以看到,终于找到了Thread,点进Worker的构造方法
	boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

	// --------------------------------------------------------
	// 进入Worker
	// 是个内部类,终于看到了工厂方法new线程的过程了
	Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
	}
	

最后找到了这个线程集合:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第7张图片
最后的最后提一下Executors.newCachedThreadPool(),它创建的还是ThreadPoolExecutor,区别在与其默认保证线程60秒存活,六十秒中内没有任务占用它,这个线程就会被释放,在之后需要再动态创建。所以在不用的状态下,CachedThreadPool不会占用任何资源。

4、ScheduledExecutorService

ScheduledExecutorService可以用来执行一些周期化的任务,或者在一个特定时间的延迟后执行。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Application {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

        long start = System.currentTimeMillis();

        scheduledExecutorService.schedule(
                () -> {
                    System.out.println("延迟执行:" + Thread.currentThread().getName());
                    System.out.println(System.currentTimeMillis() - start);
                }, 1, TimeUnit.SECONDS
        );

        scheduledExecutorService.scheduleAtFixedRate(
                () -> {
                    System.out.println("周期执行:" + Thread.currentThread().getName());
                    System.out.println(System.currentTimeMillis() - start);
                }, 0, 2, TimeUnit.SECONDS
        );
    }
}

console:
[JAVA多线程并发]Thread、Runnable、ExecutorService、ThreadPoolExecutor笔记(一)_第8张图片
关于源码原理,我去大概瞅了一眼,不是太能理解。又去百度了一些原理文章,有些机制还没能了解,之后学完再做一下源码分析吧,暂时就这样了。

你可能感兴趣的:(JAVASE)