![](https://img-blog.csdnimg.cn/20201014180756757.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
多线程
文章平均质量分 61
花园宝宝胡图图
太菜了
展开
-
最详细的reentrantlock原理
非公平锁实现原理加锁流程首先,通过CAS将状态由0改为1,如果成功了就将owner线程改为当前线程如果CAS失败,则说明有竞争,进入else分支,进入acquire()方法 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfIn.原创 2022-04-04 20:31:57 · 723 阅读 · 0 评论 -
关于AQS
全称是AbstractQueuedSynchronized,是阻塞式和相关的同步器工具的框架特点用state属性来表示资源的状态(分独享模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁 getState -获取state状态 setState -设置state状态 compareAndSetState -乐观锁机制设置state状态 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源 提供了基于FIFO的等待队列,类似于Monitor.原创 2022-04-03 20:24:35 · 371 阅读 · 0 评论 -
十分详细讲述的ThreadPoolExecutor线程池
1、线程池状态ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量,即使用一个整数的不同位来表示这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程的个数合二为一,这样就可以用一次cas原子操作进行赋值2、构造方法最大线程数目 = 核心线程数 + 救急线程数线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。 当线程数达到corePoolSize并没有线程空闲,这时再加入任务,新加的任务会被加入wo原创 2022-04-01 20:35:26 · 1960 阅读 · 0 评论 -
Unsafe关于cas的相关方法,模拟实现原子整数
因为Unsafe对象是一个私有的静态变量,只能通过反射获取 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe un原创 2022-03-29 10:09:36 · 146 阅读 · 0 评论 -
java中的原子类
++i 与 i++updateAndGet()这个方法的入参是一个IntUnaryOperator这个类是一个接口类,且被@FuntionalInterface注解,说明这个接口仅仅有一个方法需要实现,即可以配合lamda表达式使用最终i1的值为50,因此其中的x就是要被操作的数 ,此处仅仅是演示 *10,完全可以做复杂的运算,如果过程中x的值被其他线程修改,就继续while-true重新来。直到修改完结果,getAndUpdate同理,即下面这段逻辑。原子引用...原创 2022-03-26 22:59:06 · 1061 阅读 · 0 评论 -
volatile原理
volatile的底层实现原理是内存配置对volatile变量的写指令后会加入写屏障 对volatile变量的读指令前会加入读屏障如何保证可见性写屏障 保证在该屏障之前的,对共享变量的改动,都同步到主存当中,不光光是对ready同步的,而且会对上边所有的代码进行同步public void actor2(I_Result r){ num = 2; ready = true; // 此处存在写屏障}读屏障保证在该屏障之后,对共享变量的读取,加载的是主存中最.原创 2022-03-22 20:38:36 · 149 阅读 · 0 评论 -
关于有序性
JVM会在不影响正确性的前提下,调整语句的执行顺序static int i;static int j;//在某个线程内执行如下赋值操作i = ...;j = ...;对于上面的代码,先执行对i的复制,还是先执行对j的赋值,对最终的结果都不会产生影响,所以,上面代码真正被执行的时候,既可以先对i赋值,同时也可以先对j赋值这种特性被称之为【指令重排】,多线程下【指令重排】会影响正确性,为什么要有指令重排这项优化呢?假如加工一个鱼罐头,需要50分钟,只能按照一条鱼一条鱼的顺序加工原创 2022-03-22 20:02:11 · 157 阅读 · 0 评论 -
关于可见性
t线程中有一个while-true循环,我们试图通过在主方法中将run置为0,让t线程停下来,但是执行程序我们发现,并没有和我们预想的一样,t线程会停下来尝试分析这个过程初始状态,t线程刚开始从主内存读取了run的值到工作内存因为t线程要频繁的从主内存中读取run的值,JIT编译器会将run的值缓存至自己内存中的高速缓存中(底层是CPU中的缓存),减少对主存中run的访问,提高效率一秒之后,main线程修改了run的值,并同步至主内存,而t是从自己工作内存中的高速...原创 2022-03-22 14:33:44 · 209 阅读 · 0 评论 -
关于ReentranLock
相对于synchronized,它具备如下特点可中断 可以设置超时时间 可以设置为公平锁 支持多个条件变量与synchronized一样,都支持可重入基本用法public class Demo { private static ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { lock.lock(); try {原创 2022-03-21 17:59:42 · 480 阅读 · 0 评论 -
线程的活跃性
死锁一个线程需要同时获得多把锁,这时就容易发生死锁t1 线程 获得A对象锁,接下来想获取B对象的锁t2 线程 获得B对象锁,接下来想获取A对象的锁首先 t1 线程获得A对象锁,t2线程获得B对象锁,0.5秒后,t2尝试获得A对象的锁,陷入阻塞状态,再过0.5秒之后,t1 线程尝试获得B对象锁,也陷入了阻塞,发生了死锁哲学家就餐问题有五位哲学家,围坐在圆桌旁他们只做两件事,思考和吃饭,思考一会儿吃口饭,吃完饭接着思考 吃饭时要用两根筷子吃,桌上共有五根筷子,每位原创 2022-03-21 15:41:27 · 239 阅读 · 0 评论 -
线程状态的转换
New 创建了一个java线程对象,还未与操作系统的线程相关联起来1箭头 当调用线程对象的start方法,java的线程就和操作系统的线程关联起来了,状态变为runnable(运行状态+阻塞状态)2箭头 调用wait/notify 转到Waitingt线程用synchronized(obj)获取了对象锁后调用obj.wait()方法时,t线程从RUNNING --> WAITING 调用obj.notify(),obj.notifyAll(),t.intertupt()时 竞争..原创 2022-03-15 20:25:37 · 451 阅读 · 0 评论 -
如何判断对象可以回收
引用计数法:只要一个对象被其他变量所引用,就让这个变量的计数加1,如果某一个变量不再引用,就让他的计数减一。当引用计数变为0。如果两者互相引用,它们各自的引用计数都是1,就会导致虽然没有任何其他对象对他有引用,它们俩还是不会被回收。jvm没有采用这种算法可达性分析就像从盘子中拿起一串葡萄,掉到盘子上的就是要被回收的,而连在葡萄上的就是不被回收的根对象System Class JVM运行的核心类对象 Native Stack 本地方法所引用的对象 Thread 活动..原创 2022-03-12 22:37:58 · 419 阅读 · 0 评论 -
JVM -- 直接内存
并不属于JVM管理,属于系统内存,即操作系统的内存常见于NIO操作,用于数据缓冲区 分配回收成本高,但读写性能高 不受JVM内存回收管理传统的阻塞IO使用直接内存观看耗时:说明直接内存远远快于传统的方式原理 -- 为什么读写效率变高java本身并不具备磁盘读写的能力,他要使用操作系统的方法,其实就是调用native方法,会涉及到用户态和内核态的切换,当切换到内核态,就可以由cpu的函数读取磁盘文件的内容,在操作系统内存划分出一块系统缓冲区,磁盘的...原创 2022-03-12 16:15:50 · 505 阅读 · 0 评论 -
Park & Unpark
LockSupport.park();LockSupport.unpark(暂停线程对象)但是看下面的情况unpark先于park执行,这时t1线程即使调用了park方法,也没有进入阻塞,而是立马向下执行了特点与Object的wait & notify 相比wait,notify和notifyAll必须配合Object Monitior一起使用,而unpark不必 park & unpark 是以线程为单位【阻塞】和【唤醒】线程,而notify只能随机..原创 2022-03-12 12:18:56 · 381 阅读 · 0 评论 -
异步模式 -- 生产者消费者
与前边的保护性暂停中的GuardObject不同,不需要产生结果和消费结果的线程一一对应 消费队列可以用来平衡生产和消费的线程资源 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据 消费队列是有容量限制的,队满不会再加入数据,空的时候不会再消耗数据 JDK中各种阻塞队列,采用的就是这种模式public class Test { public static void main(String[] args) { MessageQueue que原创 2022-03-11 21:09:54 · 445 阅读 · 0 评论 -
多线程 -- 保护性暂停
Guarded Suspension,用在一个线程等待另一个线程的结果有一个结果需要从第一个线程传递到另一个线程,让他们关联同一个GuardedObject 如果有结果不断从一个线程到另一个线程那么就可以使用消息队列 JDK中, join的实现,Future的实现,采用的就是此模式 因为要等待另一方的结果,因此归类到同步模式public class GuardedObject { private Object response; public Object get.原创 2022-03-11 20:21:44 · 402 阅读 · 0 评论 -
使用 wait / notify 的正确姿势
public class ToTest { static final Object room = new Object(); static boolean hasCigarette = false; static boolean hasTakeout = false; public static void main(String[] args) throws InterruptedException { new Thread(() -> { .原创 2022-03-09 20:20:22 · 143 阅读 · 0 评论 -
关于 wait / notify
Owner线程发现条件不满足,调用 wait 方法,既可进入 WaitSet 变为 WAITING状态 BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用CPU时间片 BLOCKED线程会在Owner线程释放锁时唤醒 WAITING线程会在Owner线程调用 notify 或notifyAll 时唤醒,但唤醒后并不意味着立刻获得锁,仍需进入 EntryList 重新竞争API介绍obj.wait(),会让进入object监视器的线程到waitSet等待 obj.notif...原创 2022-03-09 17:31:47 · 129 阅读 · 0 评论 -
synchronized -- 偏向锁
轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行CAS操作。Java6 中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID是自己的就表示没有竞争,不用重新CAS,以后只要不发生竞争,这个对象就归该线程所有。我们看上述代码每次进行CAS,且需要创建锁记录,均会影响CPU性能对象头格式一个对象创建时:如果开启了偏向锁(默认开启),那么对象创建后,Mark Word的后三位为101,这时...原创 2022-03-09 10:53:33 · 946 阅读 · 1 评论 -
synchronized优化 -- 自旋优化
重量级锁竞争的时候,还可以使用自旋来优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞(阻塞就以为着你的线程要发生上下文切换)。自旋适用于有多核CPU的情况自旋重试成功的情况自旋重试失败的情况在Java 6 之后自旋转是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能 自旋会占用CPU时间,单核CPU自旋就是浪费,多核CPU自旋才能发挥优势 Ja..原创 2022-03-08 16:18:59 · 405 阅读 · 0 评论 -
synchronized优化 -- 锁膨胀
如果在尝试加轻量级锁的过程种,CAS操作无法成功,这时以中国情况就是有其他线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁升级为重量级锁。假设,当Thraed-1进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁Thread - 1优先使用轻量级加锁,尝试和Object中的Mark Word作交换,此时后两位是00(轻量级锁状态)。此时Thread-1加轻量级锁加锁失败,发现加锁失败,进入锁膨胀过程。、 即为Object对象申请Monitior锁,让Obj原创 2022-03-08 15:53:42 · 148 阅读 · 0 评论 -
synchronized优化 -- 轻量级锁
轻量级锁如果一个对象虽然有多线程访问,但多线程访问的时间是错开的(也就是没有相互竞争),那么可以使用轻量级锁来优化。轻量级锁对于使用者是透明的,即语法依然是synchronized当执行到method1的加锁操作的时候,会在当前线程的栈帧中,创建一个锁记录对象,其中存储了一个对象指针,和加锁对象的mark word,对象指针指向被所住的对象。让锁记录中Object reference指向锁对象,并尝试用 cas 替换 object 的 Mark Word,将Mark Word的值存入锁.原创 2022-03-08 15:14:43 · 354 阅读 · 0 评论 -
有关Moinitor
Java对象头Klass Word 是一个指针,指向当前对象的ClassNormal状态下:hashCode,哈希码age:分代年龄,年龄超过这个值,就会被转移到老年代biased_lock,偏向锁加锁状态MonitorMonitor被翻译为监视器或管程每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就被设置指向Monitor对象的指针。当我们synchroniz..原创 2022-03-07 10:59:57 · 163 阅读 · 0 评论 -
关于变量的线程安全
成员变量和静态变量是否线程安全?如果它们没有被共享,则线程安全 如果它们被共享了,根据它们的状态是否能够改变,又分为两种情况 如果只有读操作,则线程安全 如果有读写操作,则这段代码是临界区,需要考虑线程安全 局部变量是否线程安全?局部变量是线程安全的 但局部变量引用的对象则未必 如果该对象没有发生逃逸,则他是线程安全的 如果该对象发生了逃逸,需要考虑线程安全 public static void test1() { int i = 10; i++;}每个线程调用原创 2022-03-06 16:50:49 · 1027 阅读 · 0 评论 -
有关synchronized
即【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其他线程想要再获取这个【对象锁】,其他线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换。语法synchronized(对象){ // 临界区}代码这样书写,可以保证线程的安全synchronized实际是用对象锁保证了临界区代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。如果把synchro.原创 2022-03-05 21:28:01 · 359 阅读 · 0 评论 -
竞态条件和临界区
多次执行,结果均小于5000,且大部分结果均为2200左右java对静态变量的自增,自建并不是原子操作,要彻底理解,必须从字节码来进行分析例如对于i++而言(i为静态变量),实际会产生如下的JVM字节码指令:而对应i--也是类似:而Java的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:如果是单线程的情况下以上8行代码是顺序执行(不会出错)没有问题这是单线程的情况,如上图,不会发生任何问题。多线程的情况,线程2优先...原创 2022-03-04 21:21:43 · 276 阅读 · 0 评论 -
线程的状态
基于操作系统层面来讲,五种状态【初始状态】仅是在语言层面创建了线程对象,还未与操作系统相关联 即java中创建了对象,还未start()开始做个线程 【可运行状态】指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行 就绪状态,只要被分配时间片就可以被运行 【运行状态】指获取了CPU时间片运行中的状态 当CPU时间片用完,会从【运行状态】转换为【可运行状态】,会导致线程的上下文切换 【阻塞状态】 如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用...原创 2022-03-02 20:12:12 · 403 阅读 · 0 评论 -
主线程与守护线程
默认情况下,Java进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。这段程序,main方法执行结束之后,因为t1线程是while(true) 循环,并且没有线程打断他,因此他会一直处于运行状态,即main方法执行结束,t1依然再执行。此时我们将t1线程设置为守护线程,我们发现,当main方法执行结束之后,虽然t1线程的代码还没有执行结束,程序依然停止了。垃圾回收器线程就是一种守护线程 To原创 2022-03-02 15:49:41 · 639 阅读 · 1 评论 -
两阶段终止模式
在一个线程T1中如何“优雅”地终止线程T2?这里地【优雅】指的是给T2一个料理后事地机会错误思路使用线程对象的stop()方法停止线程 stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其他线程将永远无法获取锁 使用System.exit(int)方法停止线程 目的仅仅是停止一个线程,但这种做法会让整个程序都停止 两阶段终止public class Test3 { public static void main(Str.原创 2022-03-02 15:08:20 · 80 阅读 · 0 评论 -
线程有关的方法
run方法和start方法start() 方法启动一个新线程。在新的线程中运行run方法中的代码start方法只是让线程进入就绪,里边代码不一定立刻运行(CPU的时间片还没分给他)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateExceptionrun()新线程启动后会调用的方法如果在构造Thread对象时传递了Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。但可以创建Threa原创 2022-03-02 11:57:10 · 277 阅读 · 0 评论 -
线程运行的原理
栈与栈帧JVM中由堆、栈、方法区所组成,其中栈内存就是分配给线程使用的,每个线程启动后,虚拟机都会为其分配一块栈内存。每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存 ‘每个线程只能有一个活动栈帧,对应着当前正在执行的方法...原创 2022-02-26 11:17:13 · 737 阅读 · 0 评论 -
关于线程运行的查看
线程的运行是交替执行的谁先谁后,是不受我们控制的查看进程线程的方法windows操作系统下任务管理器可以查看进程和线程数,也可以原创 2022-02-25 10:35:14 · 1031 阅读 · 0 评论 -
创建线程的方式,Thraed和Runnable的关系
创建线程的几种方式 public static void test(){ Thread thread = new Thread(){ @Override public void run(){ System.out.println("running"); } }; thread.start(); } public static原创 2022-02-24 17:15:08 · 2322 阅读 · 0 评论