多线程编程:多线程并发制单的开发记录【一】

进程和线程:

下图是在来自知乎用户的解释,个人感觉狠到位

       进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

       线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

进程和线程的关系:

线程安全:

       何为线程安全?就是应用中多个线程访问某一个类(对象或方法)时,这个类始终能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。

       线程安全就是多线程访问时,采用了加锁机制(在多线程安全上加锁也是一门技术活,不是说对于共享资源你简单加个同步关键字或定义成同步方法就OK了的,锁的不合理则会大大影响程序的性能,甚至影响到业务,这里本人亲身经历过一个问题,锁粒度大小对程序的影响,参考:http://www.cnblogs.com/1315925303zxz/p/7561236.html),当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

        线程不安全呢,就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。这里的加锁机制常见的如:synchronized。

由spring管理的线程池进行并发制单的业务设计:

业务需求:多个通道进行数据采集,数据采集就是拿着VIN码去抓取数据,但是当VIN码很多时,采集的速度就很慢,所以实施多线程多并发进行采集。

1、spring管理线程池的配置,这里需要说明的一点就是核心线程数和最大线程数的配置一定要按照自己业务的并发量来设定,否则不仅不会提升并发效率,反而会出现各种数据污染的情况。

 1    <!-- 异步线程池 -->
 2     <bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
 3         <!-- 核心线程数,最小线程数 默认为1 -->
 4         <property name="corePoolSize" value="4" />
 5         <!-- 最大线程数,默认为Integer.MAX_VALUE -->
 6         <property name="maxPoolSize" value="8" />
 7         <!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
 8         <property name="queueCapacity" value="2000" /> 
 9         <!-- 线程池维护线程所允许的空闲时间,默认为60s -->
10         <property name="keepAliveSeconds" value="300" />
11         <!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
12         <property name="rejectedExecutionHandler">
13             <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
14             <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
15             <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
16             <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
17             <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
18         </property>
19     </bean>

2、线程制单业务(这里是伪代码),将符合条件的VIN码进行数据采集,否则记录到指定容器中统一处理(退单)。

初始化任务(VIN码)队列:

 1 public 
 2 class InitQueue {
 3     
 4     private static final Logger logger = LoggerFactory.getLogger(MultiThreadTask_VW_AD.class);
 5     
 6     /**
 7      *    并发队列
 8      */
 9     private static Queue<String> queue = null;
10     
11     //初始化并发队列
12     private static void initQueue(){
13         logger.info("初始化并发队列:{}");
14         if(queue == null){
15             queue = new ConcurrentLinkedQueue<String>();    //并发队列    
16         }
17         String tasklist = "JF1GH78F18G03614,JF1SH95F6AG110830,JF1SJ94D7DG010387333,JF1SH92F9CG26924,JF1SH92F5BG215090,JF1SH92F5BG222556,JF1SH92F4CG279994,JF1BR96D7CG114298,JF1BR96D0BG078632,JF1SH95F9AG094011,JF1SH98FXAG186997,JF1BM92D8BG022510,JF1BM92DXAG013855,JF1BM94D8EG0366";
18         String[] split = tasklist.split(",");
19         List<String> task = Arrays.asList(split);    //数组转集合
20         queue.addAll(task);        //按照集合中元素的顺序将集合中全部元素放进队列
21     }
22     
23     //对外提供一个任务队列
24     public static Queue<String> getQueue(){
25         initQueue();
26         return queue;
27     }
28 
29 }

制单:

 1 public 
   class MultiThreadTask_VW_AD implements Runnable,Serializable{
 4     private static final Logger logger = LoggerFactory.getLogger(MultiThreadTask_VW_AD.class);
 5     private static final long serialVersionUID = 0;
 6     
 7     private static final Object lock = new Object();    //静态锁,任务队列在某一时刻只能由一个线程操作
 8     private static List<String> errorVinList = new ArrayList<>();    //存放不合理VIN码的集合
 9     
10     private static Queue<String> taskQueue = InitQueue.getQueue();    //任务队列
11 
12     //数据采集
13     @Override
14     public void run() {
15         synchronized (lock) {
16             int queueSize = taskQueue.size();
17             if(queueSize > 0){
18                 logger.info("任务队列目前有{}个任务",queueSize);
19                 String thisVin = taskQueue.poll();
20                 logger.info("当前任务VIN码:{}",thisVin);
21                 if(thisVin.length() == 17){
22                     System.out.println("采集任务已完成【"+Thread.currentThread().getName()+"】");
23                 }else{
24                     System.out.println("不进行采集【"+Thread.currentThread().getName()+"】");
25                     errorVinList.add(thisVin);
26                     System.err.println("未采集的VIN码有:"+errorVinList);
27                 }
28             }else{
29                 logger.info("任务队列目前无任务");
30             }
31         }
32     }
33     
34 }

       再次强调:加锁的时候一定要掌握好锁的粒度,不然会影响并发效率。

3、模拟多通道进行数据采集。

 1 public 
 2 class ThreadPoolController {
 3     
 4     private static final Logger logger = LoggerFactory.getLogger(ThreadPoolController.class);
 5     
 6     /**
 7      * 一汽大众、奥迪多并发进行数据采集:
 8      */
 9     @Test
10     public void vwadTest(){
11         //加载线程池配置文件
12         ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:springs/applicationContext-threadpool.xml");
13         //通过applicationContext获取异步线程池对象threadPoolTaskExecutor
14         ThreadPoolTaskExecutor threadPool = (ThreadPoolTaskExecutor) ctx.getBean("threadPoolTaskExecutor");
15         //获取品牌制单业务对象(由spring管理的)
16         MultiThreadTask_VW_AD multiThreadTask_VW_AD = (MultiThreadTask_VW_AD) ctx.getBean("multiThreadTask_VW_AD");
17         for(int i=1; i<=15; i++){
18             threadPool.execute(multiThreadTask_VW_AD);
19         }
20         
21     }

后期继续更新多线程并发的文章,因为被这家伙给看上了,不得不深入剖析了,一直在学习的路上。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java Web

Java 面试知识点解析(二)——高并发编程篇

在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Jav...

55370
来自专栏计算机技术翻译

ElasticMQ 0.7.0:使用Akka和Spray的长轮询,非阻塞实现

原文地址:https://dzone.com/articles/elasticmq-070-long-polling-non

21990
来自专栏IT技术精选文摘

Java Instrument 功能使用及原理

22230
来自专栏编舟记

Clojure component 设计哲学

Component 是一个微型的 Clojure 框架用于管理那些包含运行时状态的软件组件的生命周期和依赖。

11100
来自专栏NetCore

Do You Know?Url Path MaxLength Limit -- 你或许以后会遇到的一个问题

最近一直在学习Asp.Net MVC,大家都知道可以用System.Web.Routing来解析一段Url,然后到达我们Route规则定下的一个Controll...

19280
来自专栏Java技术交流群809340374

史上最全Java面试266题:算法+缓存+TCP+JVM+搜索+分布式+数据库

以上是总结出的最全Java面试题目,以下是最新总结出的BAT面试java必考题目和答案。

35600
来自专栏码洞

依赖注入不是Java的专利,Golang也有

笔者在使用Golang的时候就发现构建系统依赖树非常繁琐,New了很多对象,又手工代码将它们拼接起来,写了一堆非常冗繁的代码。然后就开始想,要是Golang像J...

12610
来自专栏Golang语言社区

Go- Buffer Channel- Select-基础理解

这篇主要讲Select这个有用的关键字用来监听管道顺带讲下带缓冲的Channel 带缓冲的Channel Channel是可以带有缓冲的 c := make(c...

48680
来自专栏java架构师

BAT美团滴滴java面试大纲(带答案版)之三:多线程synchronized

继续面试大纲系列文章。   从这一篇开始,我们进入ava编程中的一个重要领域---多线程!多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸芸众生...

323100
来自专栏along的开发之旅

从源码看DL4J中Native BLAS的加载,以及配置

最近在用DeepLearning4J(DL4J)尝试语音识别的深度学习,git DL4J的代码,用IntelliJ IDEA打开,配置好相关依赖后,运行包org...

15130

扫码关注云+社区

领取腾讯云代金券