GraphQL maven USB串口通信 微服务 阿里巴巴 uitableview printing tcp view eking文件 controller vue的钩子函数 网络营销视频教程 jq选择子元素 short几个字节 bootstrap颜色 nodejs后端开发 mysql入门 python高级教程 pythoninput java配置 java中的继承 java匿名对象 java查看数据类型 java路径 java的集合 java注释规范 java抛出自定义异常 网页游戏代码 电子书之家 猫爪 subprocess 系统集成项目管理工程师教程 图片链接生成器 苹果滚动截屏 苹果剪辑 抖音代码 deepcopy 松下plc编程软件 facetime要钱吗
当前位置: 首页 > 学习教程  > 编程学习

多线程处理任务并合并数据

2021/1/9 1:51:34 文章标签: 多线程的应用场景

一、线程池创建四种方式 Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPo…

一、线程池创建四种方式

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定时线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

二、有返回值的多线程

ExecutorService接口继承自Executor,Executor中的execute方法无返回值,ExecutorService接口中的方法有返回值。

三、计数器使用

CountDownLatch也是juc包中的一个类,类似倒计时计数器,创建对象时通过构造方法设置初始值,调用CountDownLatch对象的await()方法则处于等待状态,调用countDown()方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。

有了计数器就可以暂时将主线程阻塞,等异步的多线程全部执行完毕并返回结果后,再继续执行主线程。

四、线程安全问题

有了上面线程池跟计数器的基础,现在可以动手写一个多线程处理任务并合并数据的demo了。

大致思路就是:创建一个定长的线程池,长度为10,计数器初始值也设置为10。每执行一次,将计数器减一,并且将执行结果添加到list集合中,最终多线程全部执行完毕后,计数器停止等待 主线程继续往下执行,返回list。

public static List<String> getExecutorService() throws InterruptedException{
		System.out.println("开始执行多线程...");
		long startTime = System.currentTimeMillis();
		List<String> list = new ArrayList<>();//存放返回结果
		CountDownLatch countDownLatch = new CountDownLatch(10);
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			Runnable runnable = new Runnable(){

				@Override
				public void run() {
					 try {
						Thread.sleep(3000);
                        list.add(UUID.randomUUID().toString());
                        System.out.println("当前线程name : "+Thread.currentThread().getName());
                        countDownLatch.countDown();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			};
			executorService.execute(runnable);
		}
		countDownLatch.await();
		System.out.println("submit总共cost 时间:" + (System.currentTimeMillis()-startTime)/1000 + "秒");
		executorService.shutdown();
		return list;
	}

 执行结果如下:

十个线程全部工作,但是返回值中却有null值,跟想要的结果有点出入,为啥呢?

原因在于:ArrayList是非线程安全的。ArrayList的add方法中有size++,不是一个原子操作,所以线程不安全。

 五、CopyOnWriteArrayList的用法

part4中提到的问题 解决方案很简单,将ArrayList换成CopyOnWriteArrayList即可。

	public static List<String> getExecutorService() throws InterruptedException{
		System.out.println("开始执行多线程...");
		long startTime = System.currentTimeMillis();
		List<String> list = new CopyOnWriteArrayList<>();//存放返回结果
		CountDownLatch countDownLatch = new CountDownLatch(10);
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			Runnable runnable = new Runnable(){

				@Override
				public void run() {
					 try {
						Thread.sleep(3000);
                        list.add(UUID.randomUUID().toString());
                        System.out.println("当前线程name : "+Thread.currentThread().getName());
                        countDownLatch.countDown();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			};
			executorService.execute(runnable);
		}
		countDownLatch.await();
		System.out.println("submit总共cost 时间:" + (System.currentTimeMillis()-startTime)/1000 + "秒");
		executorService.shutdown();
		return list;
	}

 

 CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器"。

优点:读操作时性能很高,因为不需要任何同步措施,适用于读多写少的并发场景。

缺点:①.每次写操作都要copy原容器,频繁的GC,内存压力大。②.由于读写分离的策略,读到的数据很可能是旧数据。


本文链接: http://www.dtmao.cc/news_show_1100010.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?