并发编程
死磕java并发编程
水无痕simon
任重而道远...
展开
-
28 AbstractQueuedSynchronizer(AQS)详解
在了解AQS之前,首先需要理解公平锁和非公平锁ReentranLock分为公平锁和非公平锁。二者的区别就在获取锁机会是否和排队顺序相关公平锁: 公平是针对锁的获取而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序就是说:如果锁被另一个线程持有,那么其他申请锁的线程会被挂起等待,加入等待队列。如果是公平的,那么就应该谁先申请锁,谁排在等待队列的最前面。后申请的就应该排在后面。当锁被释放的时候,通知等待线程再次尝试获取锁,公平锁会让最先进入队列的线程获得锁。而非公平锁则会唤醒所有.原创 2020-06-14 21:52:28 · 136 阅读 · 0 评论 -
27 JAVA原子类以及实现原理
volatile只能保证内存的可见性,取法保证原子性操作,所以java提供了各种各样的原子类以便于我们进行原子化的操作。代码如下:package com.threestone.thread;import java.util.concurrent.atomic.AtomicInteger;/**生成自增的seq 使用java原子类进行并发操作@author Administrator*/public class Seq {/**AtomicInteger: Interger.原创 2020-06-09 00:20:35 · 322 阅读 · 0 评论 -
26 再谈volatile
volatile相当于轻量级锁被volatile修饰的变量在线程之间是可见的(一个线程修改了这个变量的值,在另外一个线程之中能够读到修改后的值)synchronized也是可以保证变量被线程修改之后,线程之间的的可见性的。可见性的前提是多个线程对同一个锁进行竞争。volatile的实现原理:a. 被volatile修饰的变量,在jvm执行的时候,会比那些没有volatile修饰的变量多一个lock指令。b. 将各自线程拷贝的变量(缓存行中)经过修改之后写回主存中c. 使其他线程中拷贝的变量失.原创 2020-06-08 21:27:17 · 85 阅读 · 0 评论 -
25 深入理解自旋,死锁和重入锁 -2
锁重入: synchronized,Lock都是重入锁,所谓的重入,比如说在一个类中,有两个方法,各自都是被synchronized进行修饰,都是对这个类进行枷锁,此时有一个线程进行访问,假设调用了一个方法,并且已经获取了这个对象的锁,而在这个方法中又调用了别的方法(而这个方法也是被synchronized修饰的),那么此时不再需要重新获取锁,而是可以直接调用的。package com.threestone.thread;/**同一线程下的锁重入@author Administrator.原创 2020-06-07 20:08:42 · 123 阅读 · 0 评论 -
24 深入理解自旋,死锁和重入锁 -1
自旋是极其消耗cpu资源的(自旋就相当于while(true){} ),wait是不会消耗cpu资源的。单例模式下的锁引发的问题(此处省略部分代码):注意: 此此处距离说明的是懒汉模式下的问题,恶汉模式下不存在线程的安全性问题。//用volatile来修饰实例变量,避免jvm的指令重排序问题private static volatile Singleton instance;/*此处使用的是: 双重检查机制但是由于jvm的自行优化,那么一旦指令重排序,那么有可能会先 进行实例化操作而.原创 2020-06-07 17:18:20 · 149 阅读 · 0 评论 -
23 轻量级锁
所谓的轻量级锁就是说可以同时让多个线程进入同步代码块中****。那么轻量级的锁匙如何加锁的呢?a. 在线程执行同步代码块之前,jvm会先在当前线程的栈帧中创建用于存储锁记录的空间 。b. 栈帧:在虚拟机栈中存放是栈帧,每一个栈帧存放的是代码中方法的信息,那么这个栈帧会将markworld中存放的锁信息拷贝至当前栈帧中,就会将markworld中的锁标志位的锁信息改为轻量级锁。c. 然后开始执行同步代码块。d. 多个线程的竞争的话,某一个线程获取这个锁之后,其他的线程只能等待。e. 等待的想要执.原创 2020-06-07 11:31:49 · 138 阅读 · 0 评论 -
22 从jvm的角度谈线程的安全性问题
当我们用synchronized来同步一个代码块的时候,在jvm的层面是基于两条jvm的指令–monitorenter–monitorexit原创 2020-06-07 10:27:44 · 155 阅读 · 0 评论 -
21 从线程的优先级看待饥饿问题
某一个线程的优先级比较低,所以有可能很长时间都没有办法获取cpu的资源,最后被饿死了。从调用start()方法开始进入就绪状态,到最后进入运行状态,需要对cpu的资源进行竞争,而这种竞争是无法完全保证公平的。竞争不到的线程会被饿死,我们只能在一定程度上进行干预导致线程饿死的原因:a. 高优先级的线程会抢占低优先级线程的时间片。使其无法执行b. 线程被一直阻塞在等待获取锁的状态,而无可奈何(比如说有两个线程同时去调用一个被synchronized的代码,先获取锁的线程一直在执行而无法结束,那么那个.原创 2020-06-06 11:34:21 · 593 阅读 · 0 评论 -
20 synchronized与volatile比较
synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行synchronized在“ 主”内存区域同步整个线程的内存volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值synchronized要比volatile消耗更多资源。...原创 2020-06-04 11:55:40 · 87 阅读 · 0 评论 -
19 volatile
volatile是Java关键字,修饰变量所以当用这个关键字修饰变量的时候,就是告诉编译器,这个变量是易变的,不稳定的。所以不要试图对该变量使用缓存等优化手段。应该每次都从它的内存地址中去读取。实际上,volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的本地内存。使用volatile修饰的变量在读取的时候不需要使用锁,将会减少产生死锁的频率。volatile修饰的变量只提供了内存的可见性,并没有提供内存的原子性,就是说读取的时候去内存地址中读取,并不是每次修改..原创 2020-06-04 10:56:46 · 110 阅读 · 0 评论 -
18 spring对并发编程的支持
代码结构以及打印信息如图所示package com.paic.jmstest;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Service;/**交给spring容器进行管理@author Administrator*/@Servicepublic class Demo {@Asyncpublic void methodA() { ..原创 2020-06-02 23:16:18 · 262 阅读 · 0 评论 -
17 使用线程池来创建线程
volatile原创 2020-06-02 17:28:09 · 121 阅读 · 0 评论 -
16 死锁
有两段不同的逻辑都在等待对方释放锁才能继续往下执行的时候,就产生了死锁。class Count{//新建锁对象1private byte[] lock1 = new byte[1];//新建锁对象2private byte[] lock2 = new byte[1];public void add(){ //给lock1加锁 synchronized(lock1){ try{ //业务操作 }catch(...){ // } synchroni..原创 2020-06-02 17:18:40 · 92 阅读 · 0 评论 -
15 显示锁 StampedLock(读写锁的改进)
StampedLock是Java8引进的可以认为它是读写锁的一个改进版本,读写锁虽然分离了读和写的功能,使得读与读之间可以完全并发,但是读和写之间依然是冲突的,读锁会完全阻塞写锁,它使用的依然是悲观的锁策略.如果有大量的读线程,他也有可能引起写线程的饥饿。而StampedLock则提供了一种乐观的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞写线程。悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作读取悲观锁:在读取之前一定要判断一下,数据是否正在被修改。乐观锁:.原创 2020-06-02 17:06:24 · 122 阅读 · 0 评论 -
14 ReentrantReadWriteLock案例
分别在不同的方法中给读,写分别加锁class Count{private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();//加读锁public void get(){rwl.readLock().lock();//其它线程只能读,不能写try{//业务操作}catch(InterruptedeException e ){//…}finally{rwl.readLock.unLock();//释放锁}..原创 2020-06-02 16:36:39 · 163 阅读 · 0 评论 -
13 显示锁ReadWriteLock和ReentrantReadWriteLock
ReentrantLock比synchronized更具有伸缩性,当有多个线程竞争锁的时候,reentrantLock比synchronized的吞吐量好简而言之,就是在访问被reentranLock加锁的共享资源时,jvm将花费更少的时间来调度线程,更多的时间去执行线程。ReadWriteLock接口有两个方法:Lock readLock();Lock writeLock();这两个方法,一个用来获取读锁,一个用来获取写锁。也就是说一个资源可以被多个读线程访问,或者被一个写线程访问。..原创 2020-05-29 10:52:21 · 178 阅读 · 0 评论 -
12 ReentrantLock
EeentrantLock是唯一实现了Lock接口的类,并且提供了更多的方法使用这个类的时候,切记手动释放锁锁必须声明为一个全局变量,因为每个线程的方法都会把变量保存为一个本地的副本,如果在业务处理中声明了锁的变量,那么不同的线程获取的是不同的锁,是没有办法起到互斥的作用的,所以需要锁对象声明为全局变量,如下图所示:...原创 2020-05-28 11:53:00 · 240 阅读 · 0 评论 -
10 隐士锁(线程同步锁)synchronized
synchronized是java关键字,当它修饰一个方法或者一个代码块的时候,能够保证同一时间,只有一个线程执行该代码。说白了就是用来解决我们前面说到的并发时候确保的线程的时序性问题,确保访问顺序,讲究个先来后到吧,就看那个线程先获得这个锁对象了。用法:a. 申明在方法上: public synchronized void xxx()b. 代码块:synchronized(this){}之所以称其为隐士锁,是因为相对于显示锁来说,不需要加锁和解锁的操作。synchronized最终都是作用在.原创 2020-05-26 11:50:13 · 110 阅读 · 0 评论 -
11 显示锁Lock和ReentrantLock
说起显示锁之前,必须说一下synchronized的缺陷使用synchronized加锁以后,别的线程只能等待,等待锁释放,那么锁释放的情况有两种:(1)代码执行完毕,这种一种理想状态(2)异常,被jvm释放锁资源。如果此时获取了锁资源的线程处于io阻塞状态,那么别的线程一直等待,不太合适,因此必须要让等待线程不要一直等待,lock可以解决这个问题距离:多个线程进行读操作的时候,读写操作存在冲突,写写操作也存在冲突,但是读读的操作不应该存在冲突,但是如果是synchronized的时候,读读操作.原创 2020-05-27 11:56:23 · 127 阅读 · 0 评论 -
9 线程安全性之java内存模型
java内存模型只是一种规范,本身并不能脱离计算机的内存模型,或者说java内存模型就是利用了计算机的软硬件的体系。如图:原创 2020-05-21 14:47:19 · 88 阅读 · 0 评论 -
8 线程安全性之计算机内存
并发问题:现代计算机,并不总是从内存读取数据,数据的读取顺序依次是:寄存器,高速缓存,内存。线程在计算的时候,原始数据来自于内存,但是在计算过程中,有些数据计算频繁,可能会存放在寄存器或者高速缓存中,在计算结束以后,回写到内存中。当多个线程同时读写内存中的同一个数据时,就会出现线程的并发问题。所谓的缓存,就是将数据缓存下来,离cpu更近,所以加速了访问...原创 2020-05-21 14:44:13 · 154 阅读 · 0 评论 -
7 守护线程
守护线程可以简单的理解为后台运行线程。进城结束的时候,守护线程自然就会结束不需要手动去关心或者通知其状态守护线程与普通线程基本上没什么区别,调用线程对象的方法setDaemon(true),即可将其设置为守护线程,但是必须在启动线程之前调用...原创 2020-05-18 10:47:36 · 68 阅读 · 0 评论 -
6 并发编程杂谈
Thread(Runnable target);构造一个新线程,用于调用给定目标的run()方法void run();启动这个线程,将引发调用run(),这个方法将立即返回,并且新线程将并发运行。void run();调用关联的Runnable的run方法yeild();导致当前线程处于让步状态,如果有其他的可运行的线程与此线程同样高的优先级,那么这些线程接下来会被调度。注意,这是一个静态方法.没有可以强制终止线程的方法。但是interrupt方法可以用来请求终止线程.当对一个..原创 2020-05-13 23:24:51 · 103 阅读 · 0 评论 -
5--线程的生命周期
线程的一生一共有5种状态:new,runnable,running,blocked,deadnew thread: 当我们去new一个线程的时候,这个线程就进入了新建状态。此时这个线程未被启动,但是已经有了自己的内存空间。这个线程还没有运行,还不是活的。(我个人感觉像生物学中的受精卵????)runnable(就绪):线程已经被启动了,正在等待cpu分配时间片,这个时候已经具备了运行条件,只差cpu,处于就绪队列中。Thread调用start()方法,即可让线程处于就绪状态。这个时候的线程就是活的了(想原创 2020-05-15 17:18:17 · 102 阅读 · 0 评论 -
4 线程的中断机制
线程的中断:在理想状态下,全部的线程或者是所有的非守护线程全部运行结束的时候,程序终止。有时,我们需要为了终止某一个程序,而去停掉一个线程,有时候用户也会终止一个程序。java提供了线程的中断机制,来告诉线程,我们需要终止它。中断机制的前提是:线程需要去检查一下是否被中断,而且线程觉得是否响应这个请求,线程也可以忽略这个请求,继续执行的(有点任性)第一种方式:Thread. stop(),该方法强迫停止一个线程,并抛出一个新创建的ThreadDeath对象作为异常,这个方法已经被放弃了,不建议再使用.原创 2020-05-14 23:16:49 · 136 阅读 · 0 评论 -
基本概念-3
进程调度a) 也称短程调度(short -term-scheduing),用来决定就绪队列()中的 哪个进程应获得处理机b) 然后再由分派程序把处理机分配给该进程.进程调度为最基本的一种调度.无论是分时操作系统,批处理操作系统,还是实时操作系统,都有进程调度....原创 2020-05-12 23:35:55 · 96 阅读 · 0 评论 -
基本概念-2
进程之间的互斥与同步:进程之间的两种制约关系:(a)间接的制约:资源共享的时候,假设多个设备都需要使用打印机,但是在分时操作系统中,只有一个cpu(因为打印机是一个独占设备,你在打印的同时,别人是不会打印的),此时各个进程之间是需要相互制约的。(b)直接制约:比如说在内存中的一个缓冲区,进程A负责写入,进程B负责输出,只有当进程B获取输出之后,进程A才可以写入,这个时候进程之间是需要相互协作的,那么这就是一中直接制约的关系临界资源: 把一段时间内只允许一个进程访问的资源成为临界资源或者独占资..原创 2020-05-12 23:31:57 · 84 阅读 · 0 评论 -
基本概念-1
线程是进程中的一个实体,所以线程本身是不会独立存在的.进程是代码在数据集合上的一次运行活动进程是操作系统资源分配和调度的基本单位,cpu比较特殊,是分配到线程的,因为真正占用cpu资源的是线程.4.线程只是进程的一个执行路径,一个进程中至少有一个线程在java中,当我们启动一个main函数的时候,就是启动了一个jvm的进程,而main函数所在的线程就是主线程。试想,一个应用程序被启动,这个应用程序中会有多个执行任务,那么这个多个执行任务都是需要去获取cpu资源的,那么这多个执行任..原创 2020-05-12 23:13:08 · 488 阅读 · 0 评论