多线程开发 - 常用的基础类

一,ExecutorService  JDK内置的多线程池类

简介:提供管理终止和执行的线程管理工具,用于跟踪一个或多个异步任务。ExecutorService 可以提交继承 Thread 或者是Runable的接口对应的类

上实例

/**
 * 多线程Demo实例
 * @date :Created in 2019-07-23 19:59
 */
public class DemoExecutor extends Thread {

    private Integer demoId;

    private String demoMsg;

    public DemoExecutor(Integer demoId, String demoMsg) {
        this.demoId = demoId;
        this.demoMsg = demoMsg;
    }

    public Integer getDemoId() {
        return demoId;
    }

    public void setDemoId(Integer demoId) {
        this.demoId = demoId;
    }

    public String getDemoMsg() {
        return demoMsg;
    }

    public void setDemoMsg(String demoMsg) {
        this.demoMsg = demoMsg;
    }

    @Override
    public String toString() {
        return "DemoExecutor{" +
                "demoId=" + demoId +
                ", demoMsg=" + demoMsg +
                '}';
    }

    /**
     * 线程执行内容
     */
    @Override
    public void run() {
        try {
            System.out.println("[" + this.demoId + "] " + "[ " + this.demoMsg + " ]" + " thread start....");
            Thread.sleep(10000);
        } catch (Exception e) {
            System.out.println("线程执行异常:" + this.toString());
        }
    }
}
 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 
 4 /**
 5  * 主函数启动 6  * @date :Created in 2019-07-23 20:07
8 */ 9 public class DemoMain { 10 11 public static void main(String[] args) { 12 /** 13 * 线程池不建议这样子创建,原因如下 14 * 15 * 以下这行代码内部实现:创建重用固定数量线程的线程池在共享的无边界队列上操作。
* 在任何时候,多线程将是活动的处理任务。
16 * 如果在所有线程都处于活动状态时提交了其他任务,它们将在队列中等待,直到线程可用。
* 如果任何线程在执行期间因失败而终止
17 * 在关闭之前,如果需要,将更换一个新的。执行后续任务。 18 * 19 * 简单的说,会产生有4个线程的线程池,即使出现错误,也会重新创建,
* 这里就有在大量创建的时候,线程不会自行销毁,
20 * 且线程之前的切换,都是需要消耗资源,长时间运行会产生性能问题,
* 别急,后面类会有就更详细的说明和创建
21 * 那就是ThreadPoolExecutor直接构建参数,后续更新讲解 22 * */ 23 ExecutorService service = Executors.newFixedThreadPool(4); 24 25 //生成50个线程任务 26 for (int i = 1; i <= 50; i++) { 27 //提交线程任务 28 service.submit(new DemoExecutor(i, "I am No." + i + " task")); 29 } 30 31 /** 32 * 等待线程任务全部执行完成关闭 33 * 由于池子只有4个线程,因此其他线程会排队,直到全部线程执行完毕 34 * */ 35 service.shutdown(); 36 } 37 }

实现的效果图如下

 

小结:上面两个代码利用ExecutorService实现一个线程池,这样子可以极大提高我们日常开发的线程管理,不用手动去通过start() 去启动线程,实现业务。代码内容里面创建线程池不建议这样子创建。后续更新增加创建方式和使用细节😁

 

二,Semaphore  JDK内置的线程共享资源类

简介:该类在JDK中称之为计算信号量,通过调用 acquire() 方法可以获取许可,继续执行业务,若当前没有获得许可,则需要等待,直到获得许可才能继续执行业务。通过调用 release() 方法进行许可证的释放,让其他线程获得许可可以继续执行业务。该类通常用限制执行业务的线程数量,而不是线程的数量,只是线程没有许可。在等待获得许可继续执行。

 

常见的业务场景:如购票,银行排号,大天朝女生厕所等等。

上买票的demo

import java.util.concurrent.Semaphore;

/**
 * @date :Created in 2019-07-24 19:38
 */
public class DemoSemaphore extends Thread {

    private Integer demoId;

    private String demoMsg;

    private Semaphore semaphore;

    public Integer getDemoId() {
        return demoId;
    }

    public void setDemoId(Integer demoId) {
        this.demoId = demoId;
    }

    public String getDemoMsg() {
        return demoMsg;
    }

    public void setDemoMsg(String demoMsg) {
        this.demoMsg = demoMsg;
    }

    public Semaphore getSemaphore() {
        return semaphore;
    }

    public void setSemaphore(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public String toString() {
        return "DemoSemaphore{" +
                "demoId=" + demoId +
                ", demoMsg='" + demoMsg + '\'' +
                ", semaphore=" + semaphore +
                '}';
    }

    public DemoSemaphore(Integer demoId, String demoMsg, Semaphore semaphore) {
        this.demoId = demoId;
        this.demoMsg = demoMsg;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            //当前线程去获取许可,获取到则继续业务,获取不到则等待许可发放
            semaphore.acquire();

            System.out.println("顾客[" + this.demoId + "] 获得买票资格");
            long startTime = System.currentTimeMillis();

            //模拟正常业务执行消耗的时间
            Thread.sleep((int) (Math.random() * 10000));
            System.out.println("顾客[" + this.demoId + "] 购买完毕, 购买信息[" + this.demoMsg  + "], 花费时间:" + (System.currentTimeMillis() - startTime) + "ms");
        } catch (Exception e) {
            System.out.println("业务异常 :" + e.getMessage());
        }finally {
            //执行完毕业务,归还许可,让下面的线程可以继续获得许可执行业务
            semaphore.release();
        }
    }
}
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**
 * @date :Created in 2019-07-24 19:50
 */
public class DemoMain {

    public static void main(String[] args) {
        //创建线程池
        ExecutorService taskExecutor = DemoMain.getThreadPoolTaskExecutor();

        //创建3个信号量,即有3个窗口
        Semaphore semaphore = new Semaphore(3);

        //模拟100个人进行排队购票
        for (int i = 1; i <= 100; i++) {
            taskExecutor.submit(new DemoSemaphore(i, "火车票", semaphore));
        }
        taskExecutor.shutdown();

        //使等待进入acquire()方法的线程,不允许被中断。
        semaphore.acquireUninterruptibly(3);
        System.out.println("【售票的小姐姐总算处理完毕今天的票了!】");
        semaphore.release(3);
    }

    /**
     * 上面一个类说了线程池的创建问题,这次更新提供一下创建的方式,
     * 后续更新继续更详细的说明 ExecutorService
     * 以及spring对 ExecutorService 的封装类 ThreadPoolTaskExecutor
     */
    public static ExecutorService getThreadPoolTaskExecutor() {
        //用google的公共包创建线程工厂工具(负责创建线程)
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();

        /**
         * 创建线程池
         *
         * @param corePoolSize    核心线程数量
         * @param maximumPoolSize 池子最大的线程数量
         * @param keepAliveTime   保持活跃的时间(毫秒)
         * @param timeUnit        TimeUnit.MILLISECONDS
         * @param workQueue       超过最大线程数量时候,堵塞队列
         * @param threadFactory   线程工厂
         * @param handler         线程拒绝策略
         * */
        ExecutorService pool = new ThreadPoolExecutor(
                3,
                20,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024),
                threadFactory,
                new ThreadPoolExecutor.AbortPolicy());
        return pool;
    }
}

最终实现的效果图如下:

 

小结:通过该实例模拟程序在应对共享资源场景下的解决实际业务问题,简单说明一下线程池合理的创建方式,后续还会继续更新,并且会说明spring相关的多线程。😃

 

二,ReentrantLock 

简介:该类在JDK中线程锁,具备可重入性,即在一个获得锁的线程可以多次获取锁,本节示例未说明该特性,在调用方法lock()进行锁定,其他线程需要等待处理完毕,调用unlock()进行释放,其他线程可以申请获得锁继续处理业务逻辑。

常用的场景:秒杀

上秒杀的示例

 1 import java.util.concurrent.locks.ReentrantLock;
 2 
 3 public class DemoReentrantLock extends Thread {
 4 
 5     private static int GOOD_NUM = 3;
 6 
 7     private Integer demoId;
 8 
 9     private String demoMsg;
10 
11     private ReentrantLock reentrantLock;
12 
13     public DemoReentrantLock(Integer demoId, String demoMsg, ReentrantLock reentrantLock){
14         this.demoId = demoId;
15         this.demoMsg = demoMsg;
16         this.reentrantLock = reentrantLock;
17     }
18 
19     @Override
20     public void run() {
21         try {
22             //增加随机性
23             Thread.sleep((int) (Math.random() * 1000));
24             System.out.println("ID: " + demoId + "进入秒杀频道!");
25 
26             //获取锁,未获得锁则一直等待,直到其他线程释放该锁,才能进行获取执行业务
27             reentrantLock.lock();
28 
29             //业务处理 秒杀获取,还有奖品,则释放奖品,没有则返回已结束
30             if (DemoReentrantLock.GOOD_NUM > 0){
31                 System.out.println("恭喜ID: " + demoId + ",秒杀到了商品【" + demoMsg + "】");
32                 DemoReentrantLock.GOOD_NUM --;
33             }else {
34                 System.out.println("抱歉,我的亲爱的用户,您未秒杀到商品,期待你下一次的参与!");
35             }
36             Thread.sleep( 1000);
37         } catch (Exception e) {
38             e.printStackTrace();
39         } finally {
40             System.out.println("释放锁,让下一个用户可以进行获取秒杀机会!");
41             //释放锁,让下一个线程继续进行秒杀操作
42             reentrantLock.unlock();
43         }
44     }
45 }
 1 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 2 
 3 import java.util.concurrent.*;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class DemoMain {
 7 
 8     public static void main(String[] args) {
 9         //创建线程池
10         ExecutorService taskExecutor = DemoMain.getThreadPoolTaskExecutor();
11 
12         //创建锁
13         ReentrantLock reentrantLock = new ReentrantLock();
14 
15         //模拟100个人抢3个商品
16         for (int i = 1; i <= 100; i++) {
17             taskExecutor.submit(new DemoReentrantLock(i, "iPhone X 256G 顶配版", reentrantLock));
18         }
19         taskExecutor.shutdown();
20     }
21 
22     /**
23      * 上面一个类说了线程池的创建问题,这次更新提供一下创建的方式,
24      * 后续更新继续更详细的说明 ExecutorService
25      * 以及spring对 ExecutorService 的封装类 ThreadPoolTaskExecutor
26      */
27     public static ExecutorService getThreadPoolTaskExecutor() {
28         //用google的公共包创建线程工厂工具(负责创建线程)
29         ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
30 
31         /**
32          * 创建线程池
33          *
34          * @param corePoolSize    核心线程数量
35          * @param maximumPoolSize 池子最大的线程数量
36          * @param keepAliveTime   保持活跃的时间(毫秒)
37          * @param timeUnit        TimeUnit.MILLISECONDS
38          * @param workQueue       超过最大线程数量时候,堵塞队列
39          * @param threadFactory   线程工厂
40          * @param handler         线程拒绝策略
41          * */
42         ExecutorService pool = new ThreadPoolExecutor(
43                 10,
44                 20,
45                 0L,
46                 TimeUnit.MILLISECONDS,
47                 new LinkedBlockingQueue<Runnable>(1024),
48                 threadFactory,
49                 new ThreadPoolExecutor.AbortPolicy());
50         return pool;
51     }
52 }

最终实现的效果如下:

 

小结:通过该实例模拟程序实现在秒杀情况下,控制一个个请求排队,获取锁的即获得了秒杀的产品,其他未获得都提示未秒杀到。

posted @ 2019-07-23 20:44  LawlietL  阅读(499)  评论(0)    收藏  举报