那么对于synchronized锁呢?代码以及结果如下所示:
package com.yuxue.juc.lockDemo; public class SynchronizedDemo { public static void main(String[] args) throws InterruptedException { phone phone = new phone(); new Thread(()->{ phone.phoneTest01(); },"t1").start(); Thread.sleep(1000); new Thread(()->{ phone.phoneTest01(); },"t2").start(); } } class phone{ public synchronized void phoneTest01(){ System.out.println(Thread.currentThread().getName()+"\t invoked phoneTest01()"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } phoneTest02(); } public synchronized void phoneTest02() { System.out.println(Thread.currentThread().getName()+"\t -----invoked phoneTest02()"); } }结果为:
t1 invoked phoneTest01() t1 -----invoked phoneTest02() t2 invoked phoneTest01() t2 -----invoked phoneTest02()上述两个实验可以验证我们的ReentrantLock以及synchronized都是可重入锁!
3. 自旋锁 3.1 自旋锁概念是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
就是我们CAS一文当中提到的这段代码:
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }其中while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4))这行代码更体现出了自旋锁的核心,也就是当我尝试去拿锁的时候,一直循环,直到拿到锁为止
3.2 手写一个自旋锁试试?下面的AtomicReference可以去看CAS那篇有详细讲解
package com.yuxue.juc.lockDemo; import java.util.concurrent.atomic.AtomicReference; /** * 实现自旋锁 * 自旋锁好处,循环比较获取直到成功为止,没有类似wait的阻塞 * * 通过CAS操作完成自旋锁,t1线程先进来调用mylock方法自己持有锁2秒钟, * t2随后进来发现当前有线程持有锁,不是null,所以只能通过自旋等待,直到t1释放锁后t2随后抢到 */ public class SpinLockDemo { public static void main(String[] args) { //资源类 SpinLock spinLock = new SpinLock(); //t1线程 new Thread(()->{ //加锁 spinLock.myLock(); try { //休眠 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //解锁 spinLock.myUnlock(); },"t1").start(); //这里主线程休眠,为了让t1首先得到并加锁 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //t2线程 new Thread(()->{ spinLock.myLock(); try { //这里休眠时间较长是为了让输出结果更加可视化 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } spinLock.myUnlock(); },"t2").start(); } } class SpinLock { //构造原子引用类 AtomicReference<Thread> atomicReference = new AtomicReference<>(); //自己的加锁方法 public void myLock() { Thread thread = Thread.currentThread(); System.out.println(thread.getName() + "\t" + "come in myLock"); //自旋核心代码!!当期望值是null并且主内存值也为null,就将其设置为自己的thread,否则就死循环,也就是一直自旋 while (!atomicReference.compareAndSet(null, thread)) { } } public void myUnlock() { Thread thread = Thread.currentThread(); System.out.println(thread.getName() + "\t" + "===== come in myUnlock"); //解锁,用完之后,当期望值是自己的thread主物理内存的值也是自己的,也就是被自己的线程占用 //用完之后解锁,将主物理内存中Thread的地方设置为空,供其他线程使用 atomicReference.compareAndSet(thread, null); } }结果为:
//t1以及t2同时进入myLock方法,争夺锁使用权 t1 come in myLock t2 come in myLock //t1使用完首先释放锁 t1 ===== come in myUnlock //t2使用完释放锁,但是在5秒之后,因为在程序中我们让其休眠了5s t2 ===== come in myUnlock 4. 读写锁分为:独占锁(写锁)/共享锁(读锁)/互斥锁
4.1 概念
独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁
共享锁:只该锁可被多个线程所持有
ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁
互斥锁:读锁的共享锁可以保证并发读是非常高效的,读写、写读、写写的过程是互斥的
4.2 代码