
多线程
个人对多线程的学习手记
至学者
有道无术,术尚可求,有术无道,止于术!
展开
-
回顾Zookeeper分布式锁的原理
一 概述分布式锁可以分为共享锁和排他锁。二 分布式锁之共享锁共享锁(Shared Locks),又被称作为读锁,当某事务T1对某一数据对象D1加上共享锁,那么当前事务只能对D1进行读取操作,同时其他事务也只能对这个数据对象加上共享锁,直到该数据对象上的所有共享锁都被释放了。三 分布式锁之排他锁排他锁(Exclusive Locks),又被称作写锁和排他锁,如果事务T1对数据对象D1加上排他锁之后,那么在该数据被加锁的期间,只允许事务T1对数据对象D1进行读...原创 2022-02-27 11:49:27 · 1228 阅读 · 0 评论 -
回顾分布式锁概念
一 概述在很多场景中,我们为了保证数据的最终一致性,我们会借助分布式事务或者分布式锁来实现。二 分布式锁的使用意义在单机应用中,当我们需要对某个共享变量进行多线程同步访问的时候,我们可以借助锁的机制来完成响应资源的并发访问。但是当我们在分布式系统中,我们需要将一个应用部署到多台机器上进行负载均衡处理的时候,可能对系统中的某个变量在每台机器上都分配一个内存,当多台服务器上的应用都接收到操作同一变量的请求的时候,由于每个请求都是访问不同内存中的同一...原创 2022-02-27 11:20:10 · 175 阅读 · 0 评论 -
java.util.concurrent.CompletableFuture@b4c966a[Not completed]
一 概述当我们使用juc包中的CompletableFuture的时候可能会遇到信息如下:java.util.concurrent.CompletableFuture@b4c966a[Not completed]二 解释与解决这是提示我们异步编排任务没有完成,此时我们只需要调用阻塞方法get()即可。...原创 2022-02-18 00:23:29 · 2659 阅读 · 0 评论 -
回顾CompletableFuture 异步编排
一 概述在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过`get`方法阻塞或 者轮询的方式获得结果,但是这种方式不推荐使用。 Completabl...原创 2022-02-17 00:17:53 · 278 阅读 · 1 评论 -
Executors构建线程池的不足
一 概述在构建线程池的时候我们可以简单的通过Executors来构建,以newFixedThreadPool为例:public static ExecutorService executor = Executors.newFixedThreadPool(10);二 Executors构建线程的的不足线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的开发者更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors原创 2022-02-15 17:34:46 · 521 阅读 · 0 评论 -
回顾线程优先级
一 线程的调度分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。 抢占式调度模型:优先级高的线程先得到CPU资源,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取CPU时间片相对多一些。(Java使用的为抢占式调度模型) 随机性:假如计算机只有一个CPU,那么CPU再某一时刻只能执行一条命令,线程只能在得到CPU的时间片,即CPU的使用权的时候,才能够执行指令。 在多线程程序的执行过程中是存在随机性,因为谁抢到CPU的使用权是...原创 2021-11-16 14:07:55 · 221 阅读 · 0 评论 -
回顾造成多线程死锁的条件
一 概述“死锁”就是两个或两个以上的线程在执行过程中,互相持有对方所需要的资源,导致这些线程处于等待状态,无法继续执行。若无外力作用,它们都将无法继续执行下去,就进入了“永久”阻塞的状态。二 形成死锁的条件 1.互斥,共享的资源source1和source2同一时间只能被一个线程占用。 2. 线程1在尝试占有source2的锁的同时不会释放占有source1的锁。 3. 其他线程无法抢占线程1所持有的锁,即占有的资源。 ...原创 2021-10-28 15:32:21 · 131 阅读 · 0 评论 -
回顾线程池的执行过程
一 概述二 线程池的执行过程主线程会通过execute()方法执行一个任务,如果核心线程池没有满,则会立即创建一个线程来执行这个任务,如果核心线程已满,则会将线程放入阻塞队列(可以缓存一系列的任务),核心线程会轮询阻塞队列并获取新的任务执行,当核心线程和阻塞队列都已经满了,则会创建新的线程且不超过最大线程数,当已经达到线程最大则会通过一定的饱和策略来处理,当线程池的饱和拒绝策略为CallerRunsPollcy(调用方自行执行的策略)的时候则会回到主线程,通过主线程自己来通过执行(Runnabl原创 2021-07-27 20:38:05 · 263 阅读 · 0 评论 -
回顾线程池的几种状态
一概述二 线程池不同状态Running状态:表示线程池处于正常运行的状态,能够接收新任务,同时能够将新的任务加到处理队列中。SHUTDOWN状态:状态一的线程池执行shutDown()方法后,此时的线程池不再接受新任务,队列中的任务会被执行完毕。STOP状态:状态一的线程池执行shutDownNow()方法后,此时的线程池不再接受新任务,队列中的任务不会被执行完毕,而时被丢弃,正在执行的任务也会被丢弃。TIDYING状态:当线程池中的任务为空的时候,及所有的任务都被终止的状态。TERMINATE原创 2021-07-27 20:23:50 · 263 阅读 · 0 评论 -
回顾多线程编程——创建线程
一 概述在Java中,提供了三种创建线程的方法:通过实现Runnable接口创建线程(创建线程最简单的方法)。 通过继承Thread类创建线程。 通过Callable和Future创建线程。二通过实现Runnable接口创建线程待续......原创 2021-05-23 00:50:28 · 98 阅读 · 0 评论 -
回顾多线程编程——线程的优先级
一 概述为了有助于操作系统对线程的调度次序,每个线程都有一个优先级。二 线程优先级详情public class Thread implements Runnable { /** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assig原创 2021-05-23 00:35:17 · 237 阅读 · 0 评论 -
单独启动线程与使用线程池的区别
一 概述在我们的日常开发过程中,我们偶尔会遇到业务层中我们需要同时修改多张表的数据并且需要有序的执行,一般我们都会通过同步的方式,即单线程的方式来执行,可能会存在执行超时的异常,造成请求结果失败,即使成功了,前端也需要等待较长的时间来获取相应结果。基于上述现象,我们就可以通过线程特性来进行一定的优化。二 单独启动线程...原创 2021-05-23 00:21:32 · 966 阅读 · 0 评论 -
回顾Java多线程编程——线程生命周期
一 概述Java内置了对多线程编程的支持,使得可以通过充分利用cpu来开发出高效率的程序。一条线程是指进程中一个单一顺序的控制流,一个进程可以并发使用多个线程,每条线程则并行执行各自的任务。二 回顾进程一个进程包括由操作系统分配的内存空间,含有一个或多个线程,一个线程必须是某个进程的一部分而无法独立存在,而进程会一直运行,直到所有的非守护线程都与运行结束才能结束。三 一个线程的生命周期线程新建状态:使用java中的new关键字和Thread类或者其子类实例化出一个线程对象后,该线程对原创 2021-05-23 00:20:00 · 268 阅读 · 0 评论 -
回顾GC是如何判断哪个对象应该被回收问题
一 概述Java程序中,当一个对象没有被其它对象引用时,则该对象对于虚拟机而言就应该被回收的对象,其占用的内存空间需要被释放,同时对象也会被销毁。二 判断对象应该被回收的具体算法1. 引用计数算法该算法通过判断对象的引用数量来决定对象是否可以被回收,在此机制下,堆中每个对象都存在一个引用计数器,当一个对象创建后被分配一个引用之后则对应的引用计数器加1,当引用完成后则引用计数器相应的减1,当某个对象的引用计数器为0的时候后,则会被虚拟机作为垃圾进行回收,并释放其所占用的内存空间。优点:引用原创 2021-01-25 10:54:52 · 312 阅读 · 0 评论 -
LinkedHashSet源码阅读回顾
一 概述LinkedHashSet在Java 8中的继承关系图LinkedHashSet在Java 8中的源码public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { ......}根据源码可知,LinkedHashSet实现了Set接口,所以LinkedHashSet中的数据是不原创 2021-01-16 14:08:32 · 165 阅读 · 0 评论 -
守护线程
一 概述有时候,我们需要创建一个线程执行一些辅助工作,但又不希望这个线程阻碍JVM的关闭。此时我们就需要使用守护线程(Daemon Thread)。二 普通线程&守护线程线程分为两种:普通线程和守护线程。在JVM启动时创建的所有线程中,除了主线程以外,其他的线程都是守护线程(例如垃圾回收器以及其他执行辅助工作的线程)。当创建一个新线程时,新线程将继承创建它的线程的守护状态,因此在默认情况下,主线程创建的所有线程都是普通线程。普通线程与守护线程之间的差异仅在于当线程退出时发生的操作。当原创 2020-10-25 22:46:56 · 223 阅读 · 0 评论 -
线程封闭&栈封闭
一 概述当访问共享数据的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术被称为线程封闭(Thread Confinement),它是实现线程安全的最简单的方式之一。当某个对象封闭在一个线程之中时,这种用法将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。在Swing中大量的使用线程封闭技术。Swing的可视化组件和数据模型对象都不是线程安全的,Swing通常将它们封闭到Swing的事件分发线程中来实现线程安全性。要想正确地使原创 2020-10-25 19:27:01 · 367 阅读 · 0 评论 -
重新认识的volatile关键字
1. 加锁与可见性内置锁可以用于确保某个线程以一种可预测的方式来查看另一线程的执行结果。当线程A执行某个同步代码块时,线程B随后进入由同一个所保护的同步代码块,在这种情况下可以保证,在释放锁之前,A看到的变量值在B获得锁后同样可以由B看到。即当线程B执行由锁保护的同步代码块时,可以线程A之前在同一个同步代码块中的所有操作结果。这也符合Happen-before原则,即A Happen-before B。基于此我们在访问某个共享且可变的的变量时要求所有线程在同一锁上同步,就是为了确保写入该变量原创 2020-10-24 10:25:59 · 190 阅读 · 0 评论 -
队列在线程池等有限资源池中的应用
一 概述CPU的资源是有限的,任务的处理速度与线程个数并不是完全线性正相关的。相反,过多的线程反而会导致CPU资源频繁的切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。二 队列队列的概念非常好理解,你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的"队列"。队列是一种操作受限的线程表数据结构,存在两个基本操作:入队enqueue(),放一个数据到队列尾部;出队dequeue(),从队列头部取一个元素。原创 2020-10-02 16:51:48 · 345 阅读 · 0 评论 -
锁优化
一 概述高效并发是JDK升级到JDK6后一项重要的改进项,锁优化则是重要的改进项,目的是为了在线程之间更高效地共享数据及解决竞争问题,从而提高程序的执行效率。各种锁优化技术:适应性自旋(Adaptive Spinning) 锁消除(Lock Elimination) 锁粗化(Lock Coarseing) 轻量级锁(LinghtWeight Locking) 偏向锁(Biased Locking)二 自旋锁与自适应自旋对于互斥同步来说,互斥同步对性能最大的影响是阻塞的实现,挂起线.原创 2020-09-30 10:41:29 · 311 阅读 · 0 评论 -
如何获取子线程的执行结果
一 概述对于线程的管理,我们不仅可以通过线程池进行管理,我们还可以通过Future和Callable进行管理。二 Runnable的缺陷Runnable接口无法返回一个值返回。Runnable接口源码@FunctionalInterfacepublic interface Runnable { //void方法,不存在返回值 public abstract void run();}Runnable接口不能抛出checked Exception。三 Calla原创 2020-09-29 00:31:02 · 1007 阅读 · 0 评论 -
Java中的不变性(Immutable)和final有关吗
一 概述Java中满足同步需求的另一种方法是使用不可变的对象(Immutable Object)。因为如果对象的状态不会改变,那么并发同步过程中就不会存在并发问题。final关键字可以用来修饰变量,方法,类。final定义的引用是不可变的,但是引用的对象是可变的 。final定义的值类型是无法修改的。二 Java中的不变性Immutable如果某个对象在被创建之后其状态就不能被修改,那么这个对象就称为不可变对象。线程安全是不可变对象的固有属性之一,它们的不变性条件是构造函数创建的,只要它们的原创 2020-09-28 21:27:10 · 411 阅读 · 2 评论 -
我所知道的atomic包
一 概述对于原子操作而言,它是一个不可中断的操作,即使是在多线程环境下也能够保证操作的不可中断性。它类似与锁,且比锁更加有优势,因为粒度更加细,原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最小粒度的情况了,通常锁的粒度都比原子变量大。同时,一般情况下使用原子类的效率比使用锁的效率更高,除了某些高度竞争场景。二 原子类的种类Atomic*基本类型原子类 AtomicInteger AtomicLong AtomicBoolean Atomic*Array数组类型原原创 2020-09-28 19:54:37 · 208 阅读 · 0 评论 -
HashMap,Vector,Hashtable,ConcurrentHashMap等并发容器线程安全的进化过程
一 概述二 VectorVector线程安全的集合类,同时它是基于动态数组实现的集合类,借助Synchronized修饰的方法来保证多线程安全。在Vector集合类中Synchronized加在方法上,性能会比较差。三 通过Collections中的指定方法保证线程安全ArrayList和HashMap为线程不安全的集合类,我们可以通过Collections中提供的方法使得其能够在多线程的情况下保持线程安全。Collections.synchronizedList(new Array原创 2020-09-28 12:45:26 · 920 阅读 · 0 评论 -
Java中的不同锁
一 概述在Java5之前,在协调对共享对象的访问可以使用的内置锁机制是借助关键字synchronized和volatile实现,Java5之后增加了Lock接口,并提供了相应是实现类,如ReentrantLock类等。它是弥补内置加锁机制不适合时的一种高级功能。二 Lock接口Lock接口定义了一组抽象的加锁操作。与内置加锁机制不同的时,Lock提供了一种无条件,可轮询,定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的,故可以称这些锁位显式锁。在Lock的实现中必须提供与内部锁相同.原创 2020-09-27 13:24:13 · 359 阅读 · 0 评论 -
我意识中的AQS(AbstractQueuedSynchronizer)以及几种同步类(器)
一 概述原创 2020-09-26 22:13:08 · 242 阅读 · 0 评论 -
ReentrantLock的特点
一 概述自JDK5起,Java类库中新提供了java.util.concurrent包,其中的java.util.concurrent.locks.Lock接口便成了Java的另一种全新的互斥同步手段。基于Lock接口,用户能够以非块结构(Non-Block Structured)来实现互斥同步,从而摆脱了语言特性的束缚,改为在类库层面去实现同步,这也为日后扩展出不同的调度算法,不同特征,不同性能,不同语义的各种锁。二 ReentrantLock(重入锁)重入锁(ReenturantLock)是原创 2020-09-18 22:13:08 · 964 阅读 · 0 评论 -
Java是如何通过ThreadLocal类来实现变量的线程独享
一 概述Java中,如果一个变量要被多线程访问,可以使用volatile关键字将它声明为“易变的”;如果一个变量只要被某个线程独享时,我们可以通过java.lang.ThreadLocal类来实现线程本地存储的功能。每一个线程的Thread对象中都有一个ThreadLocalMap对象,这个对象存储了一组ThreadLocal<?>的实例化对象为键,以本地线程变量为值的K-V值对,ThreadLocal对象就是当前线程的ThreadLocalMap的访问入口,每一个ThreadLocal对象原创 2020-09-15 22:51:19 · 1302 阅读 · 0 评论 -
synchronized的个人理解
一 概述在Java中最基本的互斥同步手段就是synchronized关键字,这是一种块结构(Block Structured)的同步语法。synchronized关键字在经过Javac编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。这两个指令都需要一个类型为reference的参数来指明要锁定和解锁的对象。如果Java源码中的synchronized明确指定了对象参数,那就以这个对象的引用作为类型为reference的参数。如果没有明确指定,那就根据sy原创 2020-09-15 18:29:52 · 237 阅读 · 0 评论 -
线程池不同实例的可创建方法与停止方法
一 概述JDK中封装好的Java线程池实例有以下几种:1.newFixedThreadPool2.newSingleThreadExecutor3.newCachedThreadPool4.newScheduledThreadPool5.newWorkStealingPool二 线程池不同实例方法1. newFixedThreadPool/** * @since 1.5 * @author Doug Lea */public class Executors {原创 2020-09-06 09:39:22 · 275 阅读 · 0 评论 -
线程池中的存储队列(阻塞队列)
一 概述线程池中超过核心线程数corePoolsize的线程会放入线程池中存储队列中,存储队列是一种阻塞式队列。二 阻塞队列阻塞队列是支持阻塞插入和阻塞移除方法的队列。阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。支持阻塞的插入方法(put(E e)):当队列满时,队列会阻塞插入元素的线程,直至队列不满。支持阻塞的移除方法(take()):当队列空时,队列会阻塞移除元素的线程,直原创 2020-09-05 22:49:41 · 1457 阅读 · 0 评论 -
线程池的增加线程与拒绝线程的策略
一 概述由于系统线程池每天需要处理的任务线程不是固定不变的,会多会少,所以线程池存在自己的线程增加方式。当然,线程池的中线程的数量也是应该是有限的,因为过多的线程需要耗费很多的系统资源。二 线程池增加线程的方式当线程池中线程数小于核心线程数(corePoolSize)的时候,即使线程池中之前的任务线程处于空闲状态也会创建一个新的线程来执行新的任务。 当线程池中的线程数等于(或大于)核心线程数(corePoolSize)同时多余的线程数小于(或等于存储队列(workQueue))的时候会将多余的原创 2020-09-05 11:26:06 · 929 阅读 · 0 评论 -
线程池初识
一 概述对于一个系统来说,线程和数据库连接这些资源是非常重要的,而且它们相关线程的创建和销毁对应到的是操作系统的线程,所以反复执行相关操作是非常消耗系统资源的。当我们每次需要的时候就创建,不需要的时候就销毁的操作是非常让费资源的。所以我们可以借助缓存策略,也就是使用线程池。二 Java使用线程池的好处对于Java线程而言,每个线程都会对应到操作系统中的线程操作,所以当我们反复创建和销毁线程的时候会给操作系统带来极大的消耗,如创建时的内存消耗和销毁时的GC压力。使用了线程池之后我们就可以通过创建少原创 2020-09-04 22:06:28 · 158 阅读 · 0 评论 -
Volatile关键字的基本作用和原理
1.对于volatile关键字,首先是保持内存的可见性 1.1 内存可见性(Memory Visibility):所有的线程都能看到共享内存的最新状态。 1.2 Java中对于变量的读写是通过下列原子操作完成工作内存与主内存的数据交互: 1.lock:作用于主内存,把变量标识为线程独占状态。 2.unlock:作用于主内存,解除线...原创 2020-01-07 16:45:38 · 405 阅读 · 0 评论