企业级springboot微服务项目进阶day02

有没有思考过线程在springboot项目开发中的利用?多线程?线程池?用不到?那么假如有一个特价秒杀活动是商家自己提交的业务,起始与终止时间都是提前由前端提交商家写好的。那么我们就会发现怎么使我们多一个线程专门去控制这个特价活动的下架呢?此时线程池的概念出现在你的视野里,没错我也是,之前我并没有想过这个自动计时或者是自动计算的线程业务,所以一直做的都是简单的管理平台和基础业务。

今天来尝试一下由于没有看过类似的业务解决方法,目前就一起尝试一下,自己实现这个业务,明天再找找大佬们的思路和源码使自己对这方面的知识更加深入一些。

线程的实现方式

1、实现Runnable接口

/*测试java多线程中的Runnable接口实现多线程并发*/
class myThread2 implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            /*利用线程的抓取当前线程的反射信息,得到当前运行线程的线程名*/
            System.out.println(Thread.currentThread().getName()+"线程"+": 运行"+i+"次!");
            try {
                /*挂起时间设置*/
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

}
public class test7 {
/*这里的两个线程的优先级都一样,我们可以直接创建一个主线程*/
    public static void main(String[] args) throws InterruptedException {
        myThread2 t = new myThread2();
        Thread t1 = new Thread(t,"A");
        Thread t2 = new Thread(t,"B");
        t1.start();
        t2.start();
        for(int i=0;i<10;i++){
            System.out.println("主线程正在运行!");
        }
    }
}

2、继承Thread线程类

class mythread extends Thread{
   private String name;
   public mythread(String name){
       this.name = name;
   }

   public void run(){
       for(int i = 0; i < 5; i ++){
            System.out.println(name +"运行 :"+i);
//            try{
//                    //sleep((int)(1000*Math.random()));产生随机数,随机线程运行的时间,随机时间决定运行次数。
//                    sleep(100);
//                    //固定0.1s运行一个线程,当第一个线程运行0.1s后将其挂起运行第二个线程依次循环直至循环结束。
//            }catch(InterruptedException e){
//                    e.printStackTrace();
//            }
       }
   }
}
public class test3 {
    public static void main(String[] args){
        mythread t = new mythread("A");
        mythread t1 = new mythread("B");
        t.start();
        t1.start();
    }
}

3、实现callback接口

线程的常用方法

进入Thread的源码部分:

public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;
    private int priority;

    /* Whether or not the thread is a daemon thread. */
    private boolean daemon = false;

    /* Interrupt state of the thread - read/written directly by JVM */
    private volatile boolean interrupted;

我们可以看到他也是通过实现Runnable接口的方式

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface {@code Runnable} is used
     * to create a thread, starting the thread causes the object's
     * {@code run} method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method {@code run} is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

最后的Runnable接口也只是上了一个注解(功能性接口标记注解)与最简单的run()方法。

其次我们也知道线程的五大状态:就绪,阻塞,挂起,死亡,运行。

线程执行start方法时开始就处于就绪状态,但是是否将它直接运行这取决于它的优先级,java默认初始优先级小于等于创建该线程的父线程的优先级,这个优先级参数在线程的源代码里也能够找到:

    /**
     * The minimum priority that a thread can have.
     */
    public static final int MIN_PRIORITY = 1;

    /**
     * The default priority that is assigned to a thread.
     */
    public static final int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public static final int MAX_PRIORITY = 10;

其次我们还需要知道的是线程池的概念,通用的线程池创建方法是继承ThreadFactory

线程工厂接口实现它的newThread方法

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

其实就是线程池只是存放线程的一个位置,但是它也具备运行,关闭的两种状态。也就是说它可以统一的对线程进行管理。

下面的原生类Executors源码(篇幅太大,只复制部分源码)提供的很多创建线程池的方法

public class Executors {

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    /**
     * Creates a thread pool that maintains enough threads to support
     * the given parallelism level, and may use multiple queues to
     * reduce contention. The parallelism level corresponds to the
     * maximum number of threads actively engaged in, or available to
     * engage in, task processing. The actual number of threads may
     * grow and shrink dynamically. A work-stealing pool makes no
     * guarantees about the order in which submitted tasks are
     * executed.
     *
     * @param parallelism the targeted parallelism level
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code parallelism <= 0}
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    /**
     * Creates a work-stealing thread pool using the number of
     * {@linkplain Runtime#availableProcessors available processors}
     * as its target parallelism level.
     *
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue, using the provided
     * ThreadFactory to create new threads when needed.  At any point,
     * at most {@code nThreads} threads will be active processing
     * tasks.  If additional tasks are submitted when all threads are
     * active, they will wait in the queue until a thread is
     * available.  If any thread terminates due to a failure during
     * execution prior to shutdown, a new one will take its place if
     * needed to execute subsequent tasks.  The threads in the pool will
     * exist until it is explicitly {@link ExecutorService#shutdown
     * shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * {@code newFixedThreadPool(1)} the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue, and uses the provided ThreadFactory to
     * create a new thread when needed. Unlike the otherwise
     * equivalent {@code newFixedThreadPool(1, threadFactory)} the
     * returned executor is guaranteed not to be reconfigurable to use
     * additional threads.
     *
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created single-threaded Executor
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available, and uses the provided
     * ThreadFactory to create new threads when needed.
     *
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

创建单例线程池的方法

synchronized同步关键字,当某些资源或者字段由synchronized关键字修饰时,就代表类似于操作系统中的同步与互斥信号量:synchronized关键字加到 static静态方和 synchronized(class)代码块上都是是给 Class 类上锁。上锁之后的代码块只能由上锁线程去控制访问synchronized关键字加到实例方法上是给对象实例上锁。我们只需要对创建线程池的方法上进行加锁配合判断null等条件就可以实现单例线程池的创建。

随便写一个计时器线程:

/*计时读时间(没有更高优先级抢占等其他影响因素情况下)*/
public class TimeTask extends Thread implements Runnable {
     public int time;
     public boolean sign = false;
     public String msg = null;
     public TimeTask(int Time){
         this.time = Time;
     }

    @Override
    public void run() {
        int i = 0;
        try {
            Thread.sleep(time);
            System.out.println("---------计时结束----------");
            i = 1;
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("-----定时线程睡眠异常-------------");
        }
        if (i==1) {
            sign = true;
            msg = Thread.currentThread().getName() + "计时器线程已经停止";
            System.out.println(msg);
        }

    }
}

再加上一个创建工厂类:

public class ThreadFactory implements java.util.concurrent.ThreadFactory {
    static  int time = 0;
    @Override
    public Thread newThread(Runnable r) {
        return new TimeTask(time);
    }
}

最后是创建单例线程池:

@Component
public class ThreadPool {

    /*创建一个静态的线程池对象*/
    public static ExecutorService executorService = null;

    public  ExecutorService getThreadPool(){
        /*同步锁,只让一个线程执行下面的方法,防止资源争夺或重复操作*/
        if (executorService==null) {
            synchronized (ThreadPool.class) {
                if (executorService==null) {
                    /*指定最大容纳100个线程的线程池*/
                    executorService = Executors.newFixedThreadPool(100, new ThreadFactory());
                }
            }
            return executorService;
        }
        return executorService;
    }

    public void shut(){
        if (executorService!=null)
          executorService.shutdown();
    }

}

测试

测试单例线程池创建成功与否?

 @Test
    public  void tes01(){
        ExecutorService threadPool = this.threadPool.getThreadPool();
//        测试单例
        threadPool.shutdown();
        System.out.println(threadPool);
        this.threadPool.shut();
        System.out.println(threadPool);
        ExecutorService threadPool1 = this.threadPool.getThreadPool();
        System.out.println(threadPool1);
        this.threadPool.shut();
        System.out.println(threadPool1);
    }

运行结果

 我们可以通过句柄号判断它们的句柄是一样的,说明它们的地址信息一样,也就是说他们都是同一个实例。可以发现线程池的参数有 状态、池尺寸(我创建的是newFixedThreadpool固定容量线程池)、活动线程、队列任务、完成任务。

虽然单例成功了,但是我的计时器并没有在里面实现它的睡眠从而达到计时的作用,所以明天我也会继续研究怎么用springboot使用线程池做到计时控制的功能。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
当前课程中商城项目的实战源码是我发布在 GitHub 上的开源项目 newbee-mall (新蜂商城),目前已有 9900 多个 Star,本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 商城项目功能的讲解,让大家实际操作并实践上手一个大型的线上商城项目,并学习到一定的开发经验以及其中的开发技巧。商城项目所涉及的功能结构图整理如下: 作者寄语本课程录制于2019年,距今已有一段时间。期间,Spring Boot技术栈也有一些版本升级,比如Spring Boot 2.7.x发版、Spring Boot 3.x版本正式版本。对于这些情况,笔者会在本课程实战项目的开源仓库中创建不同的代码分支,保持实战项目的源码更新,保证读者朋友们不会学习过气的知识点。新蜂商城的优化和迭代工作不会停止,不仅仅是功能的优化,在技术栈上也会不断的增加,截止2023年,新蜂商城已经发布了 7 个重要的版本,版本记录及开发计划如下图所示。 课程特色 对新手开发者十分友好,无需复杂的操作步骤,仅需 2 秒就可以启动这个完整的商城项目最终的实战项目是一个企业级别的 Spring Boot 大型项目,对于各个段的 Java 开发者都是极佳的选择实践项目页面美观且实用,交互效果完美教程详细开发教程详细完整、文档资源齐全代码+讲解+演示网站全方位保证,向 Hello World 教程说拜拜技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,可以一步提升你的市场竞争力 课程预览 以下为商城项目的页面和功能展示,分别为:商城首页 1商城首页 2购物车订单结算订单列表支付页面后台管理系统登录页商品管理商品编辑

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ForestSpringH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值