并发编程(java锁)
2018-11-20 15:01:23 0 举报
AI智能生成
此图示对Java的多线程编程使用的一些知识点作了比较详细的总结,提供给一些java开发者参考和熟悉
作者其他创作
大纲/内容
内部锁synchronized
排他锁
synchronized特性
可见性
有序性
原子性
synchronized可以修饰方法或者代码块
synchronized修饰方法
该方法内部的代码就属于一个临界区,该方法就属于一个同步方法
一个线程对该方法内部的变量的更新就保证了原子性和可见性,
synchronized修饰代码块
一个锁句柄(一个对象的引用或者是一个可以返回对象的表达式)
synchronized关键字引导的代码块就是临界区;同步块的锁句柄可以写为this关键字,表示当前对象,
JVM 将确保锁会获得自动释放。
显式锁和解锁Lock接口
Lock接口中定义了一组抽象的加锁操作
其实现类如ReentrantLock(重入锁)
非公平锁
ReentrantLock的构造方法无参时是构造非公平锁
公平锁
线程加锁的顺序来分配的,传入一个boolean值,true时为公平锁传入一个boolean值,true时为公平锁
ReentrantLock类此外还扩展了更多的功能,如嗅探锁定、多路分支通知等
ReentrantLock获取锁定与三种方式
lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
tryLock(long timeout,TimeUnit unit),如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断,或者去干别的事情
lock 必须在 finally 块中释放
ThreadLocal
ThreadLocal用于创建线程的本地变量
ThreadLocal为每个线程维护一个本地变量:采用空间换时间
它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
volatile
volatile是一个特殊的修饰符,只有成员变量才能使用它,是java提供的一种同步手段,只不过它是轻量级的同步
子主volatile变量可以保证下一个读取操作会在前一个写操作之后发生
任何被volatile修饰的变量,都不拷贝副本到工作内存,任何 修改都及时写在主存
要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件
对变量的写操作不依赖于当前值
该变量没有包含在具有其他变量的不变式中.
保证共享资源的可见性的时候可以使用volatile替代,volatile适用于新值不依赖于就值的情形
Volatile和Synchronized四个不同点
粒度不同,前者针对变量 ,后者锁对象和类
syn阻塞,volatile线程不阻塞
syn保证三大特性,volatile不保证原子性
syn编译器优化,volatile不优化
乐观锁与悲观锁
悲观锁
在做操作之前先上锁
行锁,表锁等,读锁,写锁,synchronized关键字
在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
一个线程持有锁会导致其它所有需要此锁的线程挂起。
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险
乐观锁
不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制
Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
CAS是乐观锁技术:当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
synchronized的优化,线程自旋和适应性自旋
java’线程其实是映射在内核之上的,线程的挂起和恢复会极大的影响开销。
很多线程在等待锁的时候,在很短的一段时间就获得了锁,所以它们在线程等待的时候,并不需要把线程挂起,而是让他无目的的循环,一般设置10次。这样就避免了线程切换的开销,极大的提升了性能
锁优化
尽量不要锁住方法
缩小同步代码块,只锁数据
锁中尽量不要再包含锁
将锁私有化,在内部管理锁
进行适当的锁分解
避免死锁的技术
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
ConcurrentHashMap
ConcurrentMap接口和它的一个实现类ConcurrentHashMap
比起Hashtable和synchronizedMap来,它提供了好得多的并发性
多个读操作几乎总可以并发地执行,
同时进行的读和写操作通常也能并发地执行,
而同时进行的写操作仍然可以不时地并发进行
Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶,get,put,remove等常用操作只锁当前需要用到的桶
锁的特性
排他性
阻塞性
可重入性
替代锁的同步机制解决方案
volatile关键字
final关键字
static关键字
原子变量以及各种并发容器和框架.
策略模式
采用线程特有对象: 各个不同的线程创建各自的实例,一个实例只能被一个线程访问的对象就被称之为线程的特有对象。
只读共享:在没有额外同步的情况下,共享的只读对象可以有可以由多个线程并发访问,但是任何线程都不能修改它。
线程安全共享:线程安全的对象在其内部实现同步,多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。
保护对象:被保护的对象只能通过持有特定的锁来访问。
读写锁——(Read/WriteLock):
ReadWriteLock接口是对读写锁的抽象,其默认的实现类是ReentrantReadWriteLock。
ReadWriteLock定义了两个方法readLock()和writeLock(),分别用于返回相应读写锁实例的读锁和写锁。这两个方法的返回值类型都是Lock。
读写锁是一种改进型的排它锁,读写锁允许多个线程可以同时读取(只读)共享变量
读写锁是分为读锁和写锁两种角色的,读线程在访问共享变量的时候必须持有相应读写锁的读锁,而且读锁是共享的、多个线程可以共同持有的;
写锁是排他的,以一个线程在持有写锁的时候,其他线程无法获得相应锁的写锁或读锁。总之,读写锁通过读写锁的分离从而提高了并发性。
synchronized和ReentrantLock对比
(少量同步)资源竞争不激烈的情形下,ReentrantLock性能稍微比synchronized差点点。但是当同步非常激烈的时候
(大量同步)当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
锁状态,synchronized无法判断,而ReentrantLock可以判断
锁的类型
synchronized 可重入 不可中断 非公平
ReentrantLock 可重入 可判断 可公平(两者皆可)
锁类型
可重入锁:在执行对象中所有同步方法不用再次获得锁
可中断锁:在等待获取锁过程中可中断
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
锁的种类
偏向锁 :是一种编译解释锁。如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM 编译代码,解释执行的时候,会自动的放弃同步信息。消除 synchronized 的同步代码结果。
自旋锁 :当获取锁的过程中,未获取到。为了提高效率,JVM 自动执行若干次空循环,再次申请锁,而不是进入阻塞状态的情况。称为自旋锁。自旋锁提高效率就是避免线程状态的变更
轻量级锁:过渡锁。当偏向锁不满足,也就是有多线程并发访问,锁定同一个对象的时候,先提升为轻量级锁。也是使用标记ACC_SYNCHRONIZED 标记记录的。
重量级锁 : 同步方法和同步代码块中解释的就是重量级锁
锁的使用方式为:先提供偏向锁,如果不满足的时候,升级为轻量级锁,再不满足,升级为重量级锁。自旋锁是一个过渡的锁状态,不是一种实际的锁类型
atomic包下原子操作类总结
CAS操作(又称为无锁操作)是一种乐观锁策略
无锁操作是使用CAS(compare and swap)又叫做比较交换来鉴别线程是否出现冲突,出现冲突就重试当前操作直到没有冲突为止
CAS并不是武断的间线程挂起,当CAS操作失败后会进行一定的尝试,而非进行耗时的挂起唤醒的操作,因此也叫做非阻塞同步
原子更新操作
原子更新基本类型
AtomicBoolean:以原子更新的方式更新boolean
AtomicInteger:以原子更新的方式更新Integer
AtomicLong:以原子更新的方式更新Long
原子更新数组类型
AtomicIntegerArray:原子更新整型数组中的元素
AtomicLongArray:原子更新长整型数组中的元素
AtomicReferenceArray:原子更新引用类型数组中的元素
原子更新引用类型
AtomicReference:原子更新引用类型
AtomicReferenceFieldUpdater:原子更新引用类型里的字段
AtomicMarkableReference:原子更新带有标记位的引用类型
原子更新字段类型
AtomicIntegeFieldUpdater:原子更新整型字段类
AtomicLongFieldUpdater:原子更新长整型字段类
AtomicStampedReference:原子更新引用类型,这种更新方式会带有版本号。而为什么在更新的时候会带有版本号,是为了解决CAS的ABA问题;
收藏
收藏
0 条评论
下一页