1. 锁机制
java中的锁机制是用来处理面对多线程并发情况下数据的一致性的。在我们操作一个对象或者调用一个方法前加锁,这样当其他线程也对该对象和方法进行访问时就需要获得锁,如果该锁被其他线程持有,那么该线程则进入阻塞队列等待获得锁。这样就保证了在同一时间只有一个线程在对该对象进行操作。
java中的锁,从不同的角度有好几种划分,如:(悲观锁,乐观锁)、(公平锁,非公平锁)、(共享锁,独占锁)、(轻量级锁重量级锁)等。
2. 自旋锁
当线程持有锁时,另一个线程访问该资源会进入阻塞队列等待该线程释放锁。该操作中,线程进行了一次内核态与用户态之间的切换这种行为比较耗费时间。而自旋锁认为,上一个线程会很快的释放锁,我不需要进入阻塞队列,我只需要等一会(自旋)。
自旋过程是需要占用cpu资源的,如果长时间自旋得不到锁,则会浪费cpu的资源。因此需要设置自旋的时间,释放资源。
java利用CAS实现自旋锁。
3. 乐观锁、悲观锁
乐观锁: 对线程的安全问题总是持有乐观态度,认为每次访问数据时都不会有人来修改数据,因此就不加锁。在操作数据后会检查版本号是否与之前一致,如果不一致,则证明在操作数据期间有另一个人也修改了数据,所以会重新读取,修改,更新数据。
悲观锁: 对线程的安全问题总是持有悲观态度,认为每次访问时都会有人修改数据,于是每次操作前都会加锁。
3.1 CAS
CAS指 Compare And Swap(比较并交换),java中的乐观锁大部分是通过CAS来实现的。
CAS操作中需要三个参数:V(内存地址)、E(内存地址中因该存在的值)、N(修改后的新值)。
当我们去更新一个值的时候,会进行比较,如果内存地址中本应该存储的值与内存地址中真实存储的值一样,则将值E修改为N。否则视为修改期间有其他人动过这个值,本次更新失败,重新读取旧值。
CAS的缺点:
-
如果CAS失败,会一直进行尝试。也就是CAS的 自旋 ,如果一直失败不断循环,则会给CPU带来比较大的压力。
-
ABA问题
-
只有操作一个共享变量时具有原子性,在多个共享变量时需要加锁。
ABA问题:
假设有三个线程:1)将旧值A更新为B 。2)将旧址A更新为B 。3)将旧址B更新为A 。
此时线程1执行完毕,值更新为B。线程 2 因为一些原因发生了阻塞,导致线程 3 先执行,又将 B 更新为 A 。然后线程 2 执行,将 A 更新为 B 。
这样数据就发生了错误。但我们可以通过增加版本号来解决。每对次更新数据就更改版本号,在CAS的比较阶段比较版本号,如果版本号也一致再更新数据,否则提交失败。这样就让CAS变得更加严谨了。
CAS的自旋 :
在 java.util.concurrent.atomic 包下有一堆原子类,当并发情况下,具有排他性。即当有线程执行这些原子类中的方法时,其他线程会自旋等待。
4. 可重入锁
在同一个函数内,一个线程如果已经在外层函数获得过一次锁,那么在内层函数该线程依旧可以获得另一把锁。java的可重入锁有:synchronized 和 ReentrantLock。
5. 公平锁和非公平锁
公平锁: 在分配锁的时候考虑等待获取锁的队列优先级。先排的先分配,后来的往后稍稍。
非公平锁: 分配时不考虑队列,直接获取锁。
因为有时候任务的重要性和他所需要先耗费的时间不一定等价。如果仅仅按照排队的优先级,有时候效率会比较低。java中synchronized为非公平锁,ReentrantLock默认的lock方法为非公平锁。
6. 共享锁和独占锁
独占锁: 一次只允许一个线程访问一个资源,也叫互斥锁。
共享锁: 允许多个线程并发访问共享资源。读写锁就是共享锁。
6.1 读写锁
ReadWriteLock,读写锁内部分为读锁和写锁,读写锁允许同时有大量线程进行读操作,但同时只能有一个线程写操作。且读锁与写锁互斥,即有线程写操作时不能进行读操作。
7. synchronized
synchronized 是一个独占锁、悲观锁、可重入锁。synchronized 关键字可以修饰实例方法、静态方法、代码块。当修饰实例方法(非静态方法)时,线程访问需要持有实例(对象)的锁;修饰静态方法时,锁住的是当前类的Class对象;访问代码块时,锁住的是括号内指定的对象。
7.1 死锁
假设有两个共享资源A和B、两个线程1和2。线程1持有资源A,线程2持有资源B。但是现在线程1又想访问资源B,线程2想访问资源A。都在等待获取对方的锁,等待对方释放锁,就产生了死循环,死锁。
产生死锁的条件:
-
同一时间只能有一个线程访问该资源。
-
锁不能被其他线程抢夺,持有锁的线程只能主动释放锁。
-
线程等待获取锁时不会释放自己持有的锁。
-
互相持有资源的锁,且互相等待释放锁。(形成一个环)
8. ReentrantLock
ReentrantLock 是实现了Lock接口的类。是一个可重入锁。需要手动开启、释放锁。lock方法默认非公平锁。
ReentrantLock 通过响应中断、可轮询锁、定时锁避免了死锁问题。
ReentrantLock 和 synchronized 区别:
-
两者都是可重入锁
-
前者是显式锁,后者是隐式锁;且前者需要手动开启和释放锁
-
前者有响应中断、可轮询锁、定时锁避免了死锁问题
-
前者可以得到获取锁的状态
-
前者可以设置非公平锁
9. Volatile
Volatile是比synchronized更轻量级的同步机制。
9.1 Volatile的原理
在多个线程操作同一资源时,每个线程会先从主存中复制一个资源的副本到自己的内存空间中,再对该资源进行操作,操作结束后会立即将更新后的资源写入主存中。其他拥有此资源副本的线程会使各自内存中的该资源副本失效,并重新从主存中读取。以达到线程安全的目的。Volatile保证了可见性:即一个线程对数据做出了修改,其他线程也能知道。
Volatile和synchronized的区别:
-
Volatile只能对变量使用,synchronized可以对方法、对象和类使用
-
Volatile 仅保证了可见性,但不保证原子性。(如果运算本身就不具有原子性,如:i++)
-
Volatile 不会造成线程阻塞
-
Volatile 禁止指令重排

4391

被折叠的 条评论
为什么被折叠?



