spring单例多线程有什么问题,spring boot多线程处理
00-1010前言为什么要用多线程?Springboot中封装了多线程方法吗?如何控制工艺操作中的各种参数?corePoolSize:核心线程数:最大线程数:keepAliveTime:空闲线程存活时间:workQueue:工作队列处理程序:拒绝策略摘要。
00-1010由于我们这周大部分时间都在写原型,我们遇到的主要问题是对实际功能的理解不准确,导致多次修改原型浪费时间,这也告诉我们在开始之前一定要明确实际需求。
因为之前的会议多次提到过线程,而我对线程一无所知,所以有了下面这篇文章。
00-1010在我们开发系统的过程中,经常会处理一些比较耗时的任务(比如向数据库中插入大量数据),这时候就需要用到多线程。
00-1010可以,多线程可以直接用Spring中的@Async实现。
00-1010,通过配置线程池。
线程池ThreadPoolExecutor的执行规则如下
那么让我们考虑构建一个线程池来尝试:
@ configuration @ enableasync public类thread poolconfig实现异步配置器{/* * *核心线程池大小*/private static final int core _ pool _ size=3;/* * *可以创建的最大线程数*/Private static final int max _ pool _ size=10;/* * *最大队列长度*/Private static final int queue _ capacity=10;/* * *线程池维护线程允许的空闲时间*/Private static final int keep _ alive _ seconds=300;/* * *异步执行方法线程池* * @ return */@ override @ bean公共执行器getasynceexecutor(){ threadpooltaskmexecutor=new threadpooltaskmexecutor();executor . setmaxpoolsize(MAX _ POOL _ SIZE);executor . setcorepoolsize(CORE _ POOL _ SIZE);executor . setqueuecapacity(QUEUE _ CAPACITY);executor . setkeepaliveseconds(KEEP _ ALIVE _ SECONDS);executor . setthreadname prefix( liming test );//拒绝任务的线程池(没有线程可用)的处理策略是executor . setrejectedexecutionhandler(新线程池executor . callerronpolicy());executor.initialize()。返回执行人;}}ThreadPoolExecutor是线程池在JDK的实现。这个类实现了线程池所需的各种方法,并提供了任务提交、线程管理、监控等方法。
00-1010线程池维护的最小线程数。默认情况下,核心线程在创建后不会被回收(注意:设置allowCoreThreadTimeout=true后,空闲的核心线程如果超过生存时间也会被回收)。
大于核心线程数的线程在其空闲时间超过keepAliveTime后将被回收。
00-1010线程池中允许创建的最大线程数。
添加任务时,当核心线程数已满,线程池未达到最大线程数,且无空闲线程,工作队列已满时,创建一个新线程,然后从工作队列头取出一个任务交给新线程处理,刚刚提交的任务放在工作队列的末尾。
00-1010当一个可以回收的线程的空闲时间大于keepAliveTime时,就会被回收。
回收线:
设置allowCoreThreadTimeout=true的核心线程。线程数大于核心线程数(非核心线程)
。
workQueue:工作队列
新任务被提交后,如果核心线程数已满则会先添加到工作队列,任务调度时再从队列中取出任务。工作队列实现了BlockingQueue接口。
handler:拒绝策略
当线程池线程数已满,并且工作队列达到限制,新提交的任务使用拒绝策略处理。可以自定义拒绝策略,拒绝策略需要实现RejectedExecutionHandler接口。
JDK默认的拒绝策略有四种:
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。DiscardPolicy:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态。DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。CallerRunsPolicy:由调用线程处理该任务。
我们在非测试文件中直接使用new Thread创建新线程时编译器会发出警告:
不要显式创建线程,请使用线程池。说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者过度切换的问题
public class TestServiceImpl implements TestService { private final static Logger logger = LoggerFactory.getLogger(TestServiceImpl.class); @Override public void task(int i) { logger.info("任务: "+i); }}
@Autowired TestService testService; @Test public void test() { for (int i = 0; i < 50; i++) { testService.task(i); }
我们可以看到一切执行正常;
之后我有对线程进行了一些测试:
class TestServiceImplTest { @Test public void test() { Thread add = new AddThread(); Thread dec = new DecThread(); add.start(); dec.start(); add.join(); dec.join(); System.out.println(Counter.count); } static class Counter { public static int count = 0; } class AddThread extends Thread { public void run() { for (int i=0; i<10000; i++) { Counter.count += 1; } } } class DecThread extends Thread { public void run() { for (int i=0; i<10000; i++) { Counter.count -= 1; } } }
一个自增线程,一个自减线程,对0进行同样次数的操作,理应结果仍然为零,但是执行结果却每次都不同。
经过搜索之后发现对变量进行读取和写入时,结果要正确,必须保证是原子操作。原子操作是指不能被中断的一个或一系列操作。
例如,对于语句: n +=1; 看似只有一行语句却包括了3条指令:
读取n, n+1, 存储n;
比如有以下两个进程同时对10进行加1操作
这说明多线程模型下,要保证逻辑正确,对共享变量进行读写时,必须保证一组指令以原子方式执行:即某一个线程执行时,其他线程必须等待。
static class Counter { public static final Object lock = new Object();//每个线程都需获得锁才能执行 public static int count = 0; } class AddThread extends Thread { public void run() { for (int i=0; i<10000; i++) { synchronized(Counter.lock) { static class Counter { public static final Object lock = new Object(); public static int count = 0; } class DecThread extends Thread { public void run() { for (int i=0; i<10000; i++) { synchronized(Counter.lock) { Counter.count -= 1; } } } }
值得注意的是每个类可以设置多个锁,如果线程获取的不是同一个锁则无法起到上述功能;
springBoot中也定义了很多类型的锁,在此就不一一说明了,我们目前能做到的就是注意项目中的异步操作,观察操作所使用的线程,做到在以后项目中遇到此类问题时能及时发现问题,解决问题。
总结
到此这篇关于Spring多线程的使用及问题的文章就介绍到这了,更多相关Spring多线程使用内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。