多线程编程

多线程写作类

倒计时协调器CountDownLatch

某个线程需要等待其他线程执行到特定操作结束即可。例如:在多Web服务中,在启动指定服务时需要启动若干启动过程中比较耗时的服务,为了尽可能减少服务启动过程的总耗时,该服务会使用专门的工程线程以并发的方式去启动这些服务。但是在这些服务启动完成后,需要对这些启动的服务进行检查,之后都启动完成后,才可以启动指定服务。

注意点:

  • 确保所有CountDownLatch.
  • ()调用都位于代码中正确的位置。最好是放在finally中,避免因线程出现异常导致,导致该线程一直处于WAITING状态。
  • 等待线程在等待先决操作完成的时间指定一个时间限制CountDownLatch.await(long, TimeUnit)。在超过指定时间后,在该时间CountDownLatch的计数器仍未到达0,那么所有执行该实例的await方法的线程都会被唤醒。

栅栏(CyclicBarrier)

多个线程都到了某一点之后在一起运行。例如,在web服务中,我们需要保证多个关联服务都启动完成后,确保在进行服务间的可以正常通信。

阻塞队列

ArrayBlockingQueue
适合在生产者线程和消费者线程之间的并发程度较大的情况下使用

  • LinkedBlockingQueue
    适合在生产者线程和消费者线程之间的并发成度较低的情况下使用
  • SynchronousQueue
    适合在消费者处理能力与生产者处理能力差不多的情况下使用

限购

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;public class SemaphoreBasedChanne<E> implements Channel<E> {private final BlockingQueue<E> queue;private final Semaphore semaphore;/***** @param queue*          阻塞队列* @param flowLimit*          流量县限制数*/public SemaphoreBasedChanne(BlockingQueue<E> queue, int flowLimit) {this(queue, false, flowLimit);}public SemaphoreBasedChanne(BlockingQueue<E> queue, boolean isFair, int flowLimit) {this.queue = queue;this.semaphore = new Semaphore(flowLimit, isFair); // 是否是公平锁}/*** 注意:在代码中acquire与release总是配对出现的*/@Overridepublic void put(E product) throws InterruptedException {semaphore.acquire();try {queue.put(product);} finally {// 最好放在finally中,以免资源访问出现异常的情况下当前线程所获得配额无法返还semaphore.release();}}@Overridepublic E take() throws InterruptedException {return queue.take();}
}/*** 对传输通道的抽象* * @author Viscent Huang*/
public interface Channel<P> {/*** 往传输通道中存入一个产品* * @param product*          产品*/void put(P product) throws InterruptedException;/*** 从传输通道中取出一个产品* * @return 产品*/P take() throws InterruptedException;
}

管道:线程间的直接输入与输出

PipedOutputStream和PipedInputStream是生产者-消费者模式的一个具体例子。可以看作,一个线程的输出可作为另外一个线程的输入,而不必借用文件、数据库、网络连接等其他数据交换中介。

双缓冲与Exchanger

线程中断

并发集合

  • 快照:是在Iterator实例呗创建的那一刻待遍历对象内部结构的一个只读副本,它反映了待遍历集合的某一个时刻(即Iterator实例呗创建的那一刻)的状态。不同线程在读取数据时会获取到各自的快照,因此这些快照相当于这些线程的线程的线程特有对象。所以,这种方式下进行遍历操作的线程无须加锁就可以实现线程安全。另外,由于快照是只读的,因此这种遍历方式锁返回的Iterator实例是不支持remove方法的。优点: 遍历操作和更新操作之间互不影响,缺点当遍历的集合较大时,创建快照的直接或者渐渐开销会比较大。
  • 准实时:是指遍历操作不是针对待遍历对象的副本进行的,但又不借助锁来保障线程安全,从而使得遍历操作可以与更新操作并发进行。

几种线程安全集合类

非线层安全对象并发集合类共同接口遍历实现方式
ArrayListCopyOnWriteArrayListList快照
HashSetCopyOnWriteArraySetSet快照
LinkedListConcurrentLinkedQueueQueue准实时
HashMapConcurrentHashMapMap准实时
TreeMapConcurrentSkipListMapSortedMap准实时
TreeSetConcurrentSortedSet准实时
  • ConcurrentLinkedQueue:是Queue接口的一个线程安全实现类,他相当于LinkedList的线程安全版,可以作为Collections.synchronizedList(new ArryaList())的代替品。其内部访问共享状态变量(如队首和队尾指针)的时候并不借助锁,而是使用CAS操作来保障线程安全。因此ConcurrentLinkedQueue是非阻塞的,其使用不会导致当前线程被暂停,因此也就避免了上下文切换的开销。其更适合用于更新操作和遍历操作并发的场景,例如:生产者-消费者模式中生产者线程王队列中添加元素(产品),而消费者线程从队列中移除(消费)元素。
  • ConcurrentHashMap:是Map接口的一个线程安全实现类,它相当于HashMap的线程安全版,可以作为HashTable和Collections.synchronizedMap(new HashMap())的替代品。ConcurrentHashMap内部使用了颗粒度极小的锁来保障其线程安全。ConcurrentHashMap的读取操作基本上不会导致锁的使用。另外,默认ConcurrentHashMap可以支持16个并发更新线程(并发数量可以通过concurrencyLevel来调节,值越大开销越大,越小可能导致并发更新产生锁的争用),即这些线程可以在不导致锁的争用情况下进行并发更新。因此ConcurrentHashMap可以支持比较高的并发性,并且其锁的开销一般比较小。与HashTable的区别在于:锁的力度不同,HashTable在大多数方法上加锁导致,而ConcurrentHashMap是进行分段加锁的。
  • CopyOnWriteArrayList:是List接口的一个线程安全实现类,它相当于ArrayList的线程安全版本。其内部会维护一个实例变量用array用于引用一个数组。CopyOnWriteArrayList的更新操作是通过创建一个新的数组newArray,并把老的数组的内容不知道newArray,然后对newArray进行更新并将array引用指向newArray,因此CopyOnWriteArrayList适用遍历操作元比更新操作频繁或者不希望在遍历的时候加锁的场景。而其他场景下,我们仍然要考虑适用Collections.synchronizedList(new ArrayList())。
  • CopyOnWriteArraySet:是Set接口的一个线程安全实现类,它相当于HashSet的线程安全版,其内部使用一个CopyOnWriteArrayList实例,因此CopyOnWriteArraySet的使用场景与CopyOnWriteArrayList相似。

死锁(鹬蚌相争)

在这里插入图片描述
死锁:是一种在线程中常见活性故障,如果两个或者更多的线程因相互等待而被永远暂停。

死锁的检测

使用jdk自带的Jconsole查看死锁的线程以及代码的位置信息。
在这里插入图片描述

线程管理

线程的未捕获异常与监控

当线程的run方法抛出未被捕获的异常,run方法会退出,相应的线程也提前终止。对于线程的这种异常终止做一些监控以及补偿。

import com.dengsheng.thread.util.Debug;
import com.dengsheng.thread.util.Tools;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;public class ThreadMonitorDemo {volatile boolean inited = false;static int threadIndex = 0;final static Logger LOGGER = Logger.getAnonymousLogger();final BlockingQueue<String> channel = new ArrayBlockingQueue<String>(100);public static void main(String[] args) throws InterruptedException {ThreadMonitorDemo demo = new ThreadMonitorDemo();demo.init();for (int i = 0; i < 100; i++) {demo.service("test-" + i);}Thread.sleep(2000);System.exit(0);}public synchronized void init() {if (inited) {return;}Debug.info("init...");WokrerThread t = new WokrerThread();t.setName("Worker0-" + threadIndex++);// 为线程t关联一个UncaughtExceptionHandlert.setUncaughtExceptionHandler(new ThreadMonitor());t.start();inited = true;}public void service(String message) throws InterruptedException {channel.put(message);}private class ThreadMonitor implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {Debug.info("Current thread is `t`:%s, it is still alive:%s",Thread.currentThread() == t, t.isAlive());// 将线程异常终止的相关信息记录到日志中String threadInfo = t.getName();LOGGER.log(Level.SEVERE, threadInfo + " terminated:", e);// 创建并启动替代线程LOGGER.info("About to restart " + threadInfo);// 重置线程启动标记inited = false;init();}}// 类ThreadMonitor定义结束private class WokrerThread extends Thread {@Overridepublic void run() {Debug.info("Do something important...");String msg;try {for (;;) {msg = channel.take();process(msg);}} catch (InterruptedException e) {// 什么也不做}}private void process(String message) {Debug.info(message);// 模拟随机性异常int i = (int) (Math.random() * 100);if (i < 2) {throw new RuntimeException("test");}Tools.randomPause(100);}}// 类ThreadMonitorDemo定义结束
}

异步编程

从任务角度来看,任务分为两种:

  • 同步(Synchronous),如同接力赛跑,一个任务好比一个田径运动员,比赛时,一个运动从开始跑到下个队员位置,下一个队员接着跑。
  • 异步(Asychronous),如同接力赛跑,一个任务好比一个队伍,比赛时,队伍只负责跑,至于结果需要等组委会给最后的结果。

Java中:
假设我们用一个Runable实例task来表示一个任务,我们直接调用task.run()来执行该任务,此时这个任务就是同步任务,如果使用new Thread(task).start()调用一个专门的工作者线程来执行该任务,或者将该任务提交给Executor实例executor执行,此时该任务为异步任务。

同步与异步是相对的,它取决于任务的执行方式以及我们的观察角度。

ExecutorService的实现例
方法使用条件以及注意实现
newCachedThreadPool适用于执行大量耗时较短且提交比较频繁的任务
newFixedThreadPool此线程池核心线程数等于最大线程数,所以线程不会超时关闭,在使用该线程池的时候需要手动关闭该线程池,释放资源
newSingleThreadExecutor适用单(多)生产者-单消费者模式
newScheduledThreadPool周期性线程池,周期性执行队列中的任务,多个线程
newSingleThreadScheduledExecutor周期性线程池,周期性执行队列中的任务,单个线程

前三种类的是由 ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,workQueue)演化而来的。

  • newCachedThreadPool :: new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

    0:最大核心数为0,即SynchronousQueue中的第一队列任务会被执行,而其他的需要等前面的执行完成后才执行。
    Integer.MAX_VALUE:线程池中最大的线程数,当现在执行的线程数没有超过且没有空闲线程的时候,线程池会立即启动一个新的线程执行任务。极端情况下,在同时执行的任务数量小于Integer.MAX_VALUE时,这些提交的任务都会启动一个新的线程,此时就会造成线程的频繁切换,拖累整个系统。同时还可能会造成OOM(线程数过多,需要的的内存量过大)
    60L:线程池中的线程等待时间超过60s后,会自动销毁。

  • newFixedThreadPool::new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));

    0L: 线程不会超时,即线程不会被因超时而销毁。
    nThreads: 核心线程数与最大线程数相同,即线程池中的线程不会被销毁。当我们不再使用该线程池时,应该手动关闭线程池。尽管FinalizableDelegatedExecutorService在jvm回收的时候会关闭该线程池,但是还是及时关闭该线程池。

    static class FinalizableDelegatedExecutorServiceextends DelegatedExecutorService {FinalizableDelegatedExecutorService(ExecutorService executor) {super(executor);}protected void finalize() {super.shutdown();}
    }
    
  • newSingleThreadExecutor::new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()))

    0L: 线程不会超时,即线程不会被因超时而销毁。
    1: 最大核心数和最大线程数都只有一个,所以只能有一个线程处理任务。例如,往某个文件里面写多组数据,每组数据获取时间不同,先处理完的先写,之后数据放到线程池里处理(当然在满足效率的情况下,既可以节省内存,还可以避免一些没有必须要的问题,例如死锁)。

  • newScheduledThreadPool::new ScheduledThreadPoolExecutor(corePoolSize)和newSingleThreadScheduledExecutor::new ScheduledThreadPoolExecutor(1)

    newScheduledThreadPool 和 newSingleThreadScheduledExecutor都是周期性线程池。区别在于:核心线程可能不同
    schedule与scheduleWithFixedRate不太一样,schedule当任务执行的时间超过周期的时间,会导致后面的任务都会产生时间偏移)

异步任务的批量执行:CompletionService

没有见过,暂时不学

java内存模型

高速缓存一致性

synchronized

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/746637.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入探讨MES管理系统与MOM系统之间的关系

在制造业的信息化浪潮中&#xff0c;各种系统与技术层出不穷&#xff0c;其中MES制造执行系统和MOM制造运营管理无疑是备受瞩目的两大主角。尽管它们都是制造业信息化不可或缺的部分&#xff0c;但许多人对它们之间的区别与联系仍感到困惑。本文将对MES管理系统和MOM系统进行深…

#数据结构 线性表的顺序存储

目录 每日文案 一、线性表的定义 二、线性表的操作 顺序表的存储结构 顺序表的初始化操作 判断顺序表是否为空表 将顺序表置为空表 计算顺序表中的元素个数 取出顺序表中的对应位置元素 取出对应数值的位序 在对应位置插入元素 将对应位置的元素删除 将顺序表中的数据…

1.Python数据分析—数据分析与挖掘详讲

1.Python数据分析—数据分析与挖掘详讲 一个人简介二数据分析与挖掘概述三什么是数据分析和挖掘四数据分析与挖掘在不同领域的应用4.1医疗领域&#xff1a;4.1.1 建立疾病数据库&#xff1a;4.1.2 临床决策支持&#xff1a;4.1.3 疾病预警和监控&#xff1a; 4.2 电子商务领域&…

第12章 指针

以下内容是学习尚硅谷 12.1 指针基本介绍 1&#xff09;指针是C语言的精华&#xff0c;也是C语言的难点 2&#xff09;指针&#xff0c;也就是内存的地址&#xff1b;所谓指针变量&#xff0c;也就是保存了内存地址的变量。关于指针的基本使用&#xff0c;在讲变量的时候做了…

d2-crud-plus 使用小技巧(四)—— 搜索限制只能输入数字

需求 搜索时有些字段需要限制&#xff0c;比如只能输入数字&#xff0c;不能存在其他字符包括空格。 效果 事情焦点后先触发校验&#xff0c;在触发查询。 代码 crud.js export const crudOptions (vm) > {return {columns: [{title: 号码,key: number,search: { //…

比Let‘s Encrypt更简单更齐全的免费证书申请教程

步骤一 打开JoySSL官网&#xff0c;注册属于你的专属账号&#xff1b; 永久免费SSL证书申请地址真正完全且永久免费&#xff01;不用您花一分钱&#xff0c;SSL证书免费使用90天&#xff0c;并且还支持连续签发。JoySSL携手全球权威可信顶级根&#xff0c;自研新一代SSL证书&…

【汇编】#3 8086与数据有关的寻址方式

文章目录 操作码与操作数1. 8086处理器的与数据有关的寻址方式1.1 立即数寻址方式1.2 寄存器寻址方式 2. 有效&#xff08;偏移&#xff09;地址&#xff08;effective address&#xff0c;EA&#xff09;与缺省段寄存器选择tips:段跨越前缀2.1 直接寻址tips:直接寻址与立即寻址…

GitOps实践之Argo CD (2)

argocd 【-1】argocd可以解决什么问题? helm 部署是手动的?依赖流水线。而有时候仅仅更新一个小东西,流水线跑好久,CD真的不应该和CI耦合。不同环境的helm配置不同,手动修改问题多,可以用git管理起来,例如分不同环境用目录区分。argocd创建应用可以不通环境部署到不同集…

Seata 2.x 系列【12】高可用集群部署

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 概述2. 搭建演…

声卡喊话IP喇叭,IP网络吸顶天花喇叭

声卡喊话IP喇叭&#xff0c;IP网络吸顶天花喇叭 SV-7043VP是一款ip/sip网络吸顶喇叭&#xff0c;具有10/100M以太网接口&#xff0c;从网络接口接收网络的音频数据后播放。本网络吸顶喇叭可以与其他广播主机、服务器软件和采播主机配合使用&#xff0c;实现音频的播放&#xf…

大语言模型:Query Rewriting for Retrieval-Augmented Large Language Models

总体思路 作者首先指出大语言模型虽然取得了很好的效果&#xff0c;但是仍然存在幻觉和时间顺序混乱的问题&#xff0c;因此需要额外知识库和LLM内部知识库相结合&#xff0c;来修正&#xff1b;因此优化传统的retriever-reader的方案成为需要&#xff1b;目前的研究方案当中使…

log4cplus在Qt linux中的应用与问题解决

log4cplus在Qt linux中的应用与问题解决 背景log4cplus下载遇到问题&#xff1a;libm.so.6:undefined reference to __strtof128_nanGLIBC_PRIVATE‘解决方案编译生成在Qt工程里面添加对应依赖编译运行成功 背景 最近工作中需要用到log4cplus的日志做一些记录&#xff0c;用了…

Linux——ELK日志分析系统

实验环境 虚拟机三台CentOS 7.9&#xff0c; 组件包 elasticsearch-5.5.0.rpm elasticsearch-head.tar.gz node-v8.2.1.tar.gz phantomjs-2.1.1-linux-x86_64.tar.bz2 logstash-5.5.1.rpm kibana-5.5.1-x86_64.rpm 初始…

Lombok原理及实例(Java) - 简化JavaBean开发

Lombok 1.作用:简化javabean开发 2.使用:a.下插件 -> 如果是idea2022不用下载了,自带b.导lombok的jar包c.修改设置 1.lombok介绍 Lombok通过增加一些“处理程序”&#xff0c;可以让javabean变得简洁、快速。 Lombok能以注解形式来简化java代码&#xff0c;提高开发效…

优选算法[1]

目录 1.双指针&#xff1b; 2.滑动窗口&#xff1b; 3.二分查找&#xff1b; 4.前缀和&#xff1b; 1.双指针&#xff1b; 包括对撞指针和快慢指针(一般用来循环&#xff09;&#xff1b; 题目类型&#xff1a;移动零&#xff0c;复写零&#xff0c;快乐数&#xff0c;盛…

【UE5】动画混合空间的基本用法

项目资源文末百度网盘自取 什么是动画混合空间 混合空间分为两种: 通过一个数值控制通过两个数值控制 下面通过演示让大家更直观地了解 在Character文件夹中单击右键,选择动画(Animation),选择旧有的混合空间1D 然后选择骨骼&#xff08;动画是基于骨骼显示的,所以需要选择…

vue防止用户连续点击造成多次提交

中心思想&#xff1a;在第一次提交的结果返回前&#xff0c;将提交按钮禁用。 方法一&#xff1a;给提交按钮加上disabled属性&#xff0c;在请求时先把disabled属性改成true&#xff0c;在结果返回时改成false 方法二&#xff1a;添加loading遮罩层&#xff0c;可以直接使用e…

北京保险服务中心携手镜舟科技,助推新能源车险市场规范化

2022 年&#xff0c;一辆新能源汽车在泥泞的小路上不慎拖底&#xff0c;动力电池底壳受损&#xff0c;电池电量低。车主向保险公司报案&#xff0c;希望能够得到赔偿。然而&#xff0c;在定损过程中&#xff0c;保司发现这辆车的电池故障并非由拖底事件引起&#xff0c;而是由于…

EDM营销平台的核心功能?如何做精准营销?

EDM营销平台如何选择&#xff1f;怎么使用邮件营销平台优化发信&#xff1f; EDM营销平台以其独特的优势&#xff0c;成为了企业实现精准营销、提升品牌影响力的重要工具。那么&#xff0c;EDM营销平台究竟拥有哪些核心功能呢&#xff1f;接下来&#xff0c;AokSend就来一一探…

PyQt5---初识PyQt5相关及开发实战介绍

什么是GUI GUI是Graphical User Interface&#xff08;图形用户界面&#xff09;的缩写&#xff0c;是一种用户与计算机交互的方式&#xff0c;通过使用图形化的元素&#xff08;如按钮、窗口、菜单等&#xff09;来帮助用户完成任务。GUI使得用户可以通过鼠标、键盘等输入设备…