有没有思考过线程在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使用线程池做到计时控制的功能。