Thread - 创建

2020-07-14  本文已影响0人  face_to_face

一、概述

在java中,无论是并发还是并行都离不开多线程编程,而线程的创建和使用就是这一切的开始。以下是在工作中线程的创建和使用的总结

二、Thread 和 Runnable

2.1 Thread(不推荐使用)

线程的创建一开始就是通过继承Thread类,通过重写run()方法,来实现多线程启动任务的方法,它的缺点很明显不能进行多继承,以下是代码的实现:

public class ExtThread extends Thread{

    public ExtThread(){}

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+",run....");
    }

    public static void main(String[] args) {

        ExtThread t1 = new ExtThread();
        ExtThread t2 = new ExtThread();
        ExtThread t3 = new ExtThread();
        ExtThread t4 = new ExtThread();

        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

2.2 Runnable

通过实现Runnable接口,间接的实现多线程的编写。
为什么说间接实现?

因为,如果不通过new Thread()的方式,它还是一个普通的类,不是一个单独运行的线程。

以下是代码的实现:

public class ImpleRunnable implements Runnable {
    int count = 20;
    @Override
    public void run() {
        count--;
        System.out.println(Thread.currentThread().getName()+",run....,count = "+count);

    }

    public static void main(String[] args) {

        Thread t1 = new Thread(new ImpleRunnable());
        Thread t2 = new Thread(new ImpleRunnable());
        Thread t3 = new Thread(new ImpleRunnable());
        Thread t4 = new Thread(new ImpleRunnable());

        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

2.3 Callable

Callable接口与Runnable接口是非常相似的,但是Callable接口是一种带有返回值的线程实现方式,它启动的线程的方式也是与Runnable接口不同,本质来说Callable接口是通过启动另一个线程来获取返回值的,程序会创建两个线程来执行Callable接口的逻辑。
以下是代码的实现:

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

public class ImpleCallable implements Callable<String> {

    int count = 20;

    @Override
    public String call() throws Exception {
        count--;
        System.out.println(Thread.currentThread().getName()+",run....,count = "+count);
        return Thread.currentThread().getName();
    }

    //只有第一个线程执行了任务
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        //使用Callable接口和Runnable,除了有返回值外,
        //还是有一定的区别
        Callable<String> callable  = new ImpleCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);

        //定义三个线程进行启动,结果只有一个线程执行了任务
        /**
         * FutureTask.run()执行的时候
         * if (state != NEW ||
         *             !UNSAFE.compareAndSwapObject(this, runnerOffset,
         *                                          null, Thread.currentThread()))
         * future在执行的时候,会判断该线程是否处于初始化状态
         * 所以下面只有一个线程会执行成功,其余线程不会在进行执行,
         * 如果需要执行多个任务,就需要实例化多个FutureTask
         */
        Thread mThread  = new Thread(futureTask);
        Thread mThread2 = new Thread(futureTask);
        Thread mThread3 = new Thread(futureTask);

        mThread.start();
        mThread2.start();
        mThread3.start();
        System.out.println(futureTask.get());

    }
}

2.4 线程池(推荐)

传统的实现多线程的形式有一个弊端:

2.4.1 ThreadPoolExecutor

ThreadPoolExecutor这是一个功能很齐全的线程池,只要通过理解它的七个参数基本就能掌握这个线程池的使用:

public ThreadPoolExecutor(int corePoolSize,  //核心线程数量
                                int maximumPoolSize,//最大线程数
                                //当池中线程数大于核心线程数时,该时间为余下线程(存活线程总数-核心线程数)的最大空闲存活时间
                               long keepAliveTime,
                               TimeUnit unit,//时间单位
                               BlockingQueue<Runnable> workQueue,//工作队列
                               ThreadFactory threadFactory,//创建线程的线程工厂
                               RejectedExecutionHandler handler)//拒绝策略

这7个参数中,平常最多用到的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue.在这里我主抽出corePoolSize、maximumPoolSize和workQueue三个参数进行详解。

maximumPoolSize(最大线程数) = corePoolSize(核心线程数) + noCorePoolSize(非核心线程数);

  1. 当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。
  2. 当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。
  3. 当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。
  4. 当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。

2.4.1 其它线程池的现实

实现代码如下:

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

/**
 * 使用线程池运行线程,一共有五种线程池
 * 1. newFixedThreadPool
 * 2. newWorkStealingPool
 * 3. newSingleThreadExecutor
 * 4. newCachedThreadPool
 * 5. newScheduledThreadPool
 */
public class TestThreadPool {

    public static void main(String[] args) throws Exception{
        //TestThreadPool.testNewFixedThreadPool();
        //TestThreadPool.testNewSingleThreadExecutor();
        //TestThreadPool.testNewCachedThreadPool();
        //TestThreadPool.testNewScheduledThreadPool();
        TestThreadPool.testNewWorkStealingPool();
    }

    //配置线程池,创建一个固定线程的线程池
    public static void testNewFixedThreadPool(){
        ExecutorService ex = Executors.newFixedThreadPool(10);
        runMethod(ex);
        ex.shutdown();

    }

    //单线程池
    public static void testNewSingleThreadExecutor(){
        ExecutorService ex = Executors.newSingleThreadExecutor();
        runMethod(ex);
        ex.shutdown();
    }

    //缓存线程池 - 无限制创建线程
    public static void testNewCachedThreadPool(){
        ExecutorService ex = Executors.newCachedThreadPool();
        runMethod(ex);
        ex.shutdown();
    }

    public static void runMethod(ExecutorService ex){
        for(int i = 0;i < 5;i++) {
            ex.submit(new Runnable() {

                @Override
                public void run() {
                    for(int j = 0;j < 10;j++) {
                        System.out.println(Thread.currentThread().getName()+j);
                    }

                }
            });
        }
    }

    //newScheduledThreadPool - 定时任务
    public static void testNewScheduledThreadPool() throws InterruptedException {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        //创建一个定时任务,
        //开始一秒后执行任务,然后每隔3秒执行一次任务
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello world!");
            }
        },1,3, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(100);
        service.shutdown();
    }

    //newWorkStealingPool - ForkJoin
    public static void testNewWorkStealingPool(){
        ExecutorService service = Executors.newWorkStealingPool();
        runMethod(service);
        service.shutdown();
    }

}
上一篇下一篇

猜你喜欢

热点阅读