JAVA锁

公平锁和非公平锁

//默认是非公平锁,可以设置为公平锁 
Lock lock = new ReentrantLock();
public ReentrantLock() {
	sync = new NonfairSync();
}

公平锁:多个线程按照申请的顺序获取锁

非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,有可能出现后申请的线程比先申请的线程优先获取到锁,上来就尝试占有锁,如果失败,排到队尾

synchronized 也是非公平锁

可重入锁和递归锁

可重入锁就是递归锁。

同一线程,外层函数获取到锁之后,内层函数仍然能获取到该锁

ReentrantLock 和 synchronized 都是可重入锁

可重入锁的优点:可以避免死锁

public static void main(String[] args) {
    Phone phone = new Phone();
    for (int i = 0; i < 10; i++) {
        new Thread(()->{
            phone.call();
        }).start();
    }
}

class Phone {
    //外层同步方法
    public synchronized void call(){
        System.out.println(Thread.currentThread().getId()+"线程调用call方法");
        //内层同步方法
        senMessage();
    }
    public synchronized void senMessage(){
        System.out.println(Thread.currentThread().getId()+"线程调用senMessage方法");
    }
}

自旋锁

线程不会立即阻塞,

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;
}

手写一个自旋锁

public class SpinLock {
    //原子引用的线程
    AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();

    public void lock() {
        Thread thread = Thread.currentThread();
        //没有设置成功就会一直自旋
        do {
            System.out.println(Thread.currentThread().getName() + " try lock....");
            //期望是null,就把当前的线程设置进去
        } while (!atomicReference.compareAndSet(null, thread));
        System.out.println(Thread.currentThread().getName() + " invoke lock method");
    }

    public void unLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + " invoke unLock method");
    }


    public static void main(String[] args) {
        SpinLock spinLock = new SpinLock();
        new Thread(() -> {
            spinLock.lock();
            // 获取到锁后 sleep 5秒钟
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 5秒钟之后释放锁
            spinLock.unLock();
        }, "AAA").start();

        //保证AAA线程第一次获取锁
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            // 1秒钟之后BBB线程进来,一直尝试获取锁,直到AAA五秒钟之后释放锁才能获取到锁
            spinLock.lock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取到锁1秒钟之后释放锁
            spinLock.unLock();
        }, "BBB").start();
    }
}

自旋锁如果AAA线程执行的时间较长的话,BBB线程会一直尝试获取锁,从而消耗CPU的性能

读写锁

ReentrantReadWriteLock 读:共享锁 写:互斥锁

手写MyCache

public static void main(String[] args) throws InterruptedException {
    MyCache myCache = new MyCache();
    for (int i = 0; i < 10; i++) {
        String finalI = String.valueOf(i);
        new Thread(() -> {
            myCache.add(finalI, UUID.randomUUID().toString());
        }).start();
    }
    TimeUnit.SECONDS.sleep(5);
    for (int i = 0; i < 10; i++) {
        String finalI = String.valueOf(i);
        new Thread(() -> {
            myCache.get(finalI);
        }).start();
    }
}


class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void add(String key, String value) {
        //写锁
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "正在进行写操作:" + key);
            TimeUnit.MILLISECONDS.sleep(300);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }

    }

    public void get(String key) {
        //读锁
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "正在进行读操作");
            TimeUnit.MILLISECONDS.sleep(300);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取完成" + o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }
}

执行结果:

Thread-0正在进行写操作:0
Thread-0写入完成
Thread-4正在进行写操作:4
Thread-4写入完成
Thread-7正在进行写操作:7
Thread-7写入完成
Thread-5正在进行写操作:5
Thread-5写入完成
Thread-6正在进行写操作:6
Thread-6写入完成
Thread-3正在进行写操作:3
Thread-3写入完成
Thread-2正在进行写操作:2
Thread-2写入完成
Thread-9正在进行写操作:9
Thread-9写入完成
Thread-1正在进行写操作:1
Thread-1写入完成
Thread-8正在进行写操作:8
Thread-8写入完成
Thread-10正在进行读操作
Thread-13正在进行读操作
Thread-15正在进行读操作
Thread-19正在进行读操作
Thread-18正在进行读操作
Thread-11正在进行读操作
Thread-17正在进行读操作
Thread-14正在进行读操作
Thread-12正在进行读操作
Thread-16正在进行读操作
Thread-19读取完成0b0f8d36-ec36-4877-9cac-d9f37059ea71
Thread-12读取完成1a502d57-f383-489f-9d33-c4c17017c35e
Thread-18读取完成6fae0d7e-9427-477b-9ace-c85f73d20fef
Thread-14读取完成a9816ce5-4e78-4c66-988b-a2faaeff7a0b
Thread-17读取完成da6509ba-1c38-4b94-b9e2-3ab50f0c4d85
Thread-11读取完成abdf55fc-7866-4a70-8f97-d9569c49cad1
Thread-13读取完成647bb4ca-ed09-4d71-8cce-2ca87d48a01d
Thread-15读取完成91f3c60c-bcee-4539-9b61-50521073989b
Thread-10读取完成f8d43475-afa2-41e7-971b-c34969237d3c
Thread-16读取完成3a74c83b-8d1b-4298-9c22-315741b5fe4c

写操作始终保证同一个线程

posted @ 2021-09-30 16:15  紫川先生  阅读(32)  评论(0编辑  收藏  举报