美文网首页
Thread - 创建

Thread - 创建

作者: face_to_face | 来源:发表于2020-07-14 17:34 被阅读0次

    一、概述

    在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();
        }
    
    }
    

    相关文章

      网友评论

          本文标题:Thread - 创建

          本文链接:https://www.haomeiwen.com/subject/ifnoqktx.html