2019年互联网(1.5)

一.JUC多线程及并发包

1.5.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁

公平锁和非公平锁

公平锁 是指多个线程按照申请锁的顺序来获取锁类似排队打饭 先来后到

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

两者的区别

公平锁/非公平锁 并发包ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或者非公平锁 默认是非公平锁

Lock lock =new ReentrantLock(boolean b)//默认为非公平锁,false为非公平锁,true为公平锁

Java ReentrantLock而言,通过构造哈希数指定该锁是否是公平锁 默认是非公平锁 非公平锁的优点在于吞吐量必公平锁大.

对于synchronized而言 也是一种非公平锁.

可重入锁(又名递归锁)

可重入锁,指的是同一线程外层函数获取锁以后,内层递归函数任然会获取该锁代码,

再同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁

也就是说,线程可以进入任何一个它以及拥有的锁所同步着的代码块

参考8锁理解

​ ReentrantLock/synchronized就是一个典型的可重入锁

可重入锁最大的作用就是避免死锁


可重入锁 ReenterLockDemo_1_5_1

package com.ybzn._01.juc;

class Phone {
    public synchronized void sendSms () throws Exception {
        System.out.println(Thread.currentThread().getName() + "\tsendSms");
        sendEmail();
    }

    public synchronized void sendEmail () throws Exception {
        System.out.println(Thread.currentThread().getName() + "\tsendEmail");
    }
}

/**
 * Description: *  可重入锁(也叫做递归锁) *  指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 *  在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁 * *  也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块 * * @author veliger@163.com * @date 2019-04-12 23:36
 **/
public class ReenterLockDemo_1_5_1 {
 
    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();
    }
}

结果分析: 先输出线程A的所有内容,然后再输出线程B的所有函数

image-20210214234552776

可重入锁ReenterLock_1_5_2.java

package com.ybzn._01.juc;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Phone_1 implements Runnable {
    Lock lock = new ReentrantLock();

    @Override
    public void run () {
        get();
    }

    private void get () {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\tinvoked get()");
            set();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void set () {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t invoked set()");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

/**
 * @author Hugo
 * @time 2021/2/14
 * Description: * 可重入锁(也叫做递归锁) *
 * 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 * 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁
 * 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块
 * Case two
 * ReenterLock 可重入锁
 *
 */

/** *  * * @author veliger@163.com * @date 2019-04-12 23:36 **/
public class ReenterLockDemo_1_5_2 {
    public static void main (String[] args) {
        Phone_1 phone_1 = new Phone_1();
        Thread thread3 = new Thread(phone_1, "t3");
        Thread thread4 = new Thread(phone_1, "t4");

        thread3.start();
        thread4.start();
    }
}

结果分析: 采用ReenterLock同样可以实现可重入锁

image-20210214235623939

自旋锁

unsafe类+CAS思想(自旋)

spinlock:

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


代码SpinLockDemo_1_6.java

package com.ybzn._01.juc;

import sun.awt.windows.ThemeReader;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Hugo
 * @time 2021/2/15
 * <p>
 * 题目: 实现一个自旋锁
 * 自旋锁的好处, 循环比较获取,直到成功为止,没有类型wait的阻塞
 * 当CAS操作完成自旋锁,A线程先进来调用MyLock方法自己持有锁5秒中,B随后进来发现
 * 当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后,B才随后抢到
 */

public class SpinLockDemo_1_6 {
    // 原子 引用线程
    static AtomicReference <Thread> threadAtomic = new AtomicReference <>();

    public void myLock () {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + " \t come in");

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

        }
    }

    public void myUnlock () {
        Thread thread = Thread.currentThread();
        boolean b = threadAtomic.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + " \t invoked myUnlock()");
    }

    public static void main (String[] args) {
        SpinLockDemo_1_6 demo16 = new SpinLockDemo_1_6();
        new Thread(() -> {
            demo16.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo16.myUnlock();
        }, "t1").start();

        try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace(); }

        new Thread(() ->
        {
            demo16.myLock();
            demo16.myUnlock();
        }, "t2").start();
    }

}

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

多给线程同时读一个资源类没有任何问题,所以为了满洲并发量,读取共享资源可以同时进行,

但是

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

小总结:

  •  读-读  可以共存
    
  •  读-写  不能共存
    
  •  写-写  不能共存
    

代码演示ReadWriteLockDemo_1_6_2.java

img

image-20210215012128790

读写锁

posted @ 2021-02-27 17:46  Hugo_nice  阅读(61)  评论(0)    收藏  举报