1、公平锁/非公平锁
公平锁:
是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先到先得。
非公平锁:
是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象
举个例子:如果是公平锁,那么各个线程交替执行。非公平锁就是看哪个线程牛逼抢到的快准狠就它执行,不交替。看代码:
设置公平锁:
public class fairLockDemo {
static Lock lock = new ReentrantLock(true);
public static void m(){
for (int i = 1; i <=3; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
new Thread(()->{ fairLockDemo.m();
},"t1").start();
new Thread(()->{
fairLockDemo.m();
},"t2").start();
new Thread(()->{
fairLockDemo.m();
},"t3").start();
}
}
输出结果: 三个线程轮流执行
非公平锁:lock版本
Lock lock = new ReentrantLock(false); //这里false不填也可以,默认非公平
执行结果: 谁跑的快就有糖吃
非公平锁:synchronized 版本
public class fairLockDemo {
public static void m(){
for (int i = 1; i <=3; i++) {
synchronized (fairLockDemo.class) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
public static void main(String[] args) {
for (int i = 1; i <=10 ; i++) {
new Thread(()->{ fairLockDemo.m();
},"t"+String.valueOf(i)).start();
}
}
}
执行结果:
总结:synchronized 非公平锁
ReentrantLock 默认非公平锁。也是相比syn的优势之一。
2、自旋锁(CAS底层原理)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
代码:开启两个线程,对某个变量进行修改。t2 必须等到t1 先赋值之后置null之后才可以执行,否则while循环判断。
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thead = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t come in");
//自旋锁比较
while(!atomicReference.compareAndSet(null,thead)){}
}
public void myUnLock(){
Thread thead = Thread.currentThread();
atomicReference.compareAndSet(thead,null);
System.out.println(Thread.currentThread().getName()+"\t unlock");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
//睡一秒,让t2 自旋 判断
System.out.println(Thread.currentThread().getName()+"\t 在执行睡1秒");
try { TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.myUnLock();
},"t1").start();
new Thread(()->{
spinLockDemo.myLock();
System.out.println(Thread.currentThread().getName()+"\t 循环判断好多好多鱼之后我终于可以执行了");
spinLockDemo.myUnLock();
},"t2").start();
}
}
执行结果:
3、独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁:指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁
共享锁:指该锁可以被多个线程所持有。
针对独占锁和共享锁使用业务比较单一,有没有什么锁可以在写的时候是写锁,而读到时候就是共享锁呢。很明显哪个魔鬼式式考虑到的,所以jdk8里面对ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁。
代码验证:写入的时候只允许一个写,读的时候可以多个读
public class MyCache {
private volatile Map<String,Object> map = new HashMap<>();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key,Object value){
readWriteLock.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);
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+"\t 写入完成");
}catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t正在读取:");
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e){ e.printStackTrace(); }
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+"\t读取完成:"+result);
}catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i<=5 ; i++) {
final int temp=i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i<=5 ; i++) {
final int temp=i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
运行结果: