java多线程锁总结
java线程锁
公平锁/非公平锁
公平锁:多个线程按照申请锁的顺序来获取锁。(先来后到原则)
非公平锁: 多个线程按照获取锁的顺序并不是按照申请锁的顺序。有可能后申请的线程先获取锁。在高并发的情况下,有可能会造成优先级反转或有饥饿现象(较公平锁的有点:吞吐量比公平锁大)
Lock lock = new ReentrantLock(); //构造方法无参则默认false 即 非公平锁
Lock lock = new ReentrantLock(boolean fair);//传参 true 即公平锁
可重入锁(递归锁)
线程可以进入任何一个它已经拥有锁所同步着的代码块(套娃模式)
作用:可避免死锁发生
package com.springboot.atguigu.demo;
import ch.qos.logback.core.util.TimeUtil;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 可重用锁(递归锁)
*/
class Phone implements Runnable{
public synchronized void sendSMS(){
System.out.println(Thread.currentThread().getName()+"\t sendSMS()");
sendEmail();
}
public synchronized void sendEmail(){
System.out.println(Thread.currentThread().getName()+"\t sendEmail()");
}
//===================
Lock lock = new ReentrantLock(); //公平锁
@Override
public void run() {
get();
}
/**
* 小知识点 当加多把锁时 编译及运行不会报错
* 当锁和解锁不对应时 会死锁
*/
public void get(){
lock.lock();
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t get()");
set();
}finally {
lock.unlock();
lock.unlock();
}
}
public void set(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t set()");
}finally {
lock.unlock();
}
}
}
public class ReenterLockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(new Runnable() {
@Override
public void run() {
phone.sendSMS();
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
phone.sendSMS();
}
},"t2").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
System.out.println();
System.out.println();
System.out.println();
Thread t3 = new Thread(phone);
Thread t4 = new Thread(phone);
t3.start();
t4.start();
}
}
运行结果:
控制台一直处于运行状态
自旋锁
指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处时减少线程上下文切换的消耗,缺点时循环会消耗CPU
手写自旋锁会是一道面试题尽量多手写几遍让自己会写!!!
总结:自旋锁时通过CAS(compareAndSet 比较并交换)实现 CPU并发原语
CAS:判断内存某个位置的值是否为预期值,如果时则更新为新值,过程原子。
原语的执行必须是连续的,在执行过程过不允许被打断,CAS是一条CPU的原语指令,不会造成所谓的数据不一致问题。
package com.springboot.atguigu.demo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁
*/
public class SpinLockDemo {
static AtomicReference<Thread> reference = new AtomicReference<>();
public static void mylock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t" + "come in");
while (!reference.compareAndSet(null,thread)){
}
}
public static void MyUnlock(){
Thread thread = Thread.currentThread(); // bu
reference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName()+"\t" + " invoked MyUnlock...");
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
mylock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyUnlock();
}
},"AA").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
mylock();
MyUnlock();
}
},"BB").start();
}
}
读写锁
独占锁(写锁)共享锁(读锁)互斥锁
独占锁:指该锁一次只能被一个线程所持有。对ReenTrantLock和Synchronized都是独占锁
共享锁:该锁可被多个线程所持有。对ReentantReadWriteLock其读锁是共享锁,写是独占锁
读锁的共享锁可保证并发读是高效的,读写、写写 、写读过程互斥是互斥锁
package com.springboot.atguigu.demo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key,Object value){
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在写入" + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成");
}finally {
rwLock.writeLock().unlock();
}
}
public void get(String key){
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在读取");
try {
TimeUnit.SECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读取完成" + o);
}finally {
rwLock.readLock().unlock();
}
}
}
/**
* 读-读 能共存
* 读-写 不能共存
* 写-写 不能共存
*
* 写操作:原子+ 独占 整个过程必须是一个完成的统一体,中间不允许被分割 被打断。
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i <= 5 ; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
myCache.put(finalI + "",finalI);
}
},i + "").start();
}
for (int i = 0; i <= 5 ; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
myCache.get(finalI + "");
}
},i + "").start();
}
}
}
第一次写文章。。。