java 自旋锁_java中锁你知道哪些?请手写一个自旋锁

公平和非公平锁公平锁:是指多个线程按照申请的顺序来获取值

非公平锁:是指多个线程获取值的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优选获取锁,在高并发的情况下,有可能造成优先反转或者饥饿现象

两者区别:

公平锁:

在并发环境下,每一个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个就占有锁,否则就加入等待队列中,以后会按照FIFO的规则获取锁

非公平锁:

比较粗鲁,一上来就尝试占有锁,如果失败再进行排队,也就是采用类似公平锁中方式

并发包中ReentrantLock创建指定构造函数的boolean来得到公平锁或者非公平锁,默认是非公平锁;Synchronized也是一种非公平锁,非公平锁的优点是吞吐量比公平锁大

可重入锁和不可重入锁可重入锁:指的是同一个线程外层函数获得锁之后,内层仍然能获取该锁,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取该锁

不可重入锁:所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞;

代码实现:

可重入锁:ReentranLock和synchronized都是典型的可重入锁,可重入锁最多的作用就是避免死锁

// synchronizedclass Phone {

public synchronized void sendSMS() throws Exception {

System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS");

sendEmail();

}

public synchronized void sendEmail() throws Exception {

System.out.println(Thread.currentThread().getName() + "\t ############ invoked sendEmail");

}

}

public class ReenterLockDemo {

public static void main(String[] args) {

Phone phone = new Phone();

new Thread(() -> {

try {

phone.sendSMS();

} catch (Exception e) {

e.printStackTrace();

}

}, "t1").start();

new Thread(() -> {

try {

phone.sendSMS();

} catch (Exception e) {

e.printStackTrace();

}

}, "t2").start();

}

/**** t1 invoked sendSMS t1线程在外层获取锁的时候* t1 ############ invoked sendEmail t1线程在进入内层方法会自动获取锁* t2 invoked sendSMS* t2 ############ invoked sendEmail**/

}

// ReentranLockclass Phone implements Runnable {

public synchronized void sendSMS() throws Exception {

System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS");

sendEmail();

}

public synchronized void sendEmail() throws Exception {

System.out.println(Thread.currentThread().getName() + "\t ############ invoked sendEmail");

}

Lock lock = new ReentrantLock(false);

@Override

public void run() {

get();

}

public void get() {

lock.lock();

try {

System.out.println(Thread.currentThread().getName() + "\t invoked get()");

set();

} finally {

lock.unlock();

}

}

public void set() {

lock.lock();

try {

System.out.println(Thread.currentThread().getName() + "\t ############ invoked set()");

} finally {

lock.unlock();

}

}

}

public class ReenterLockDemo {

public static void main(String[] args) {

Phone phone = new Phone();

new Thread(() -> {

try {

phone.sendSMS();

} catch (Exception e) {

e.printStackTrace();

}

}, "t1").start();

new Thread(() -> {

try {

phone.sendSMS();

} catch (Exception e) {

e.printStackTrace();

}

}, "t2").start();

try {

TimeUnit.SECONDS.sleep(1L);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println();

System.out.println();

System.out.println();

System.out.println();

Thread t3 = new Thread(phone,"t3");

Thread t4 = new Thread(phone,"t4");

t3.start();

t4.start();

}

/**** t1 invoked sendSMS t1线程在外层获取锁的时候* t1 ############ invoked sendEmail t1线程在进入内层方法会自动获取锁* t2 invoked sendSMS* t2 ############ invoked sendEmail**/

}

不可重入锁:ReentranLock和synchronized都是典型的可重入锁

独占锁(写锁)/共享锁(读锁)/互斥锁

独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁

共享锁:指该锁可以被多个线程所持有,对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁

读锁的共享锁可保证并发是非常高效的,读写,写读,写写的过程是互斥的

总结:

多个线程同事读取一个资源类没有问题,所以为了满足并发量,读取共享资源应该同时进行

如果有一个线程想去写共享资源,就不应该再有其他线程对该资源进行读或写

读和读能共存

读和写不能共存

写和写不能共存

案例:

class Mycache{

private volatile Map map = new HashMap<>();

private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

public void put(String key, Object value) {

rwLock.writeLock().lock();

System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);

try {

TimeUnit.MILLISECONDS.sleep(300);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

rwLock.writeLock().unlock();

}

System.out.println(Thread.currentThread().getName() + "\t 写入完成:");

}

public void get(String key) {

rwLock.readLock().lock();

try {

System.out.println(Thread.currentThread().getName() + "\t 正在读取:" );

try {

TimeUnit.MILLISECONDS.sleep(300);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "\t 读取完成:"+key);

} finally {

rwLock.readLock().unlock();

}

}

}

public class ReadWriteLockDemo {

public static void main(String[] args) {

Mycache mycache = new Mycache();

for (int i = 1; i <= 5; i++) {

int tempInt = i;

new Thread(() -> {

mycache.put(tempInt + "", tempInt + "");

}, String.valueOf(i)).start();

}

for (int i = 1; i <= 5; i++) {

int tempInt = i;

new Thread(() -> {

mycache.get(tempInt + "");

}, String.valueOf(i)).start();

}

}

}

自旋锁(spnlock):

UnSafe类+CAS思想(自旋)

AtomicInteger

CAS->UnSafe->CAS底层思想->ABA->原子引用更新->如果规避ABA问题

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样做的好处是减少线程上下文的切换的消耗,缺点是循环会消耗CPU

如图:

手写一个自旋锁:

public class SpinLockDemo {

// 原子引用线程 AtomicReference atomicReference= new AtomicReference<>();

public void MyLock(){

Thread thread=Thread.currentThread();

System.out.println(Thread.currentThread().getName()+"\t come in ");

while (!atomicReference.compareAndSet(null,thread)){

}

}

public void MyUnLock(){

Thread thread =Thread.currentThread();

System.out.println(Thread.currentThread().getName()+"\t invoked MyUnLock ");

atomicReference.compareAndSet(thread,null);

}

public static void main(String[] args) {

SpinLockDemo spinLockDemo=new SpinLockDemo();

new Thread(()->{

spinLockDemo.MyLock();

try {

TimeUnit.SECONDS.sleep(5);

} catch (InterruptedException e) {

e.printStackTrace();

}

spinLockDemo.MyUnLock();

},"AAA").start();

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

new Thread(()->{

spinLockDemo.MyLock();

try {

TimeUnit.SECONDS.sleep(5);

} catch (InterruptedException e) {

e.printStackTrace();

}

spinLockDemo.MyUnLock();

},"BBB").start();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值