2022-03-15 10:04阅读: 59评论: 0推荐: 0

Java 中的锁

公平锁和非公平锁

简介

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

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

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

对于 Synchronized 而言,也是一种非公平锁。

区别

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

非公平锁:非公平锁比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式。

可重入锁(递归锁)

简介

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

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

好处就是可以避免死锁

代码实践

synchronized

public class LockDemo {

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

class Phone {
    public synchronized void sendSMS() throws Exception {
        System.out.println(Thread.currentThread().getId() + "\t invoked sendSMS");
        sendEmail();
    }

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

输出

12	 invoked sendSMS
12	 invoked sendEmail
13	 invoked sendSMS
13	 invoked sendEmail

Lock

public class LockDemo{

    public static void main(String[] args) {
        Phone phone = new Phone();
        Thread thread1 = new Thread(phone);
        Thread thread2 = new Thread(phone);
        thread1.start();
        thread2.start();
    }


}

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

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

    private void get() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getId() + "\t invoked set");
            set();
        }finally {
            lock.unlock();
        }
    }

    private void set() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getId() + "\t invoked set");
        }finally {
            lock.unlock();
        }
    }
}

输出

12	 invoked set
12	 invoked set
13	 invoked set
13	 invoked set

自旋锁

简介

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

代码实践

public class SpinLockDemo {
    AtomicReference <Thread> atomicReference = new AtomicReference <>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName());
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

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

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myLock();
        }, "aa").start();

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

        new Thread(() -> {
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        }, "bb").start();
    }
}

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

简介

共享锁:对 ReentrantReadWriteLock 其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

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

多个线程同时读一个资激是没有在何问划,所以为了满足并发量,读取共享资源应该可以同时进行。

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

小总结:

  1. 读-读能共存
  2. 读-可不能共存
  3. 写-写不能其存

代码实操

未加读写锁

class MyCache {

    private volatile Map <String, Object> maps = new HashMap <>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {

            System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            maps.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 完成写入:");

    }

    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "\t 正在读取:");
        try {
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Object value = maps.get(key);
        System.out.println(Thread.currentThread().getName() + "\t 完成读取:" + value);
    }
}

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.put(finalI + "", finalI);
            }, String.valueOf(i)).start();
        }

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get(finalI + "");
            }, String.valueOf(i)).start();
        }
    }
}

输出

0	 正在写入:0
1	 正在写入:1
4	 正在写入:4
2	 正在写入:2
3	 正在写入:3
0	 正在读取:
1	 正在读取:
2	 正在读取:
3	 正在读取:
4	 正在读取:
4	 完成写入:
1	 完成写入:
2	 完成读取:2
1	 完成读取:null
0	 完成读取:null
3	 完成写入:
4	 完成读取:null
3	 完成读取:null
0	 完成写入:
2	 完成写入:

加上读写锁

class MyCache {

    private volatile Map <String, Object> maps = new HashMap <>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            maps.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 完成写入:");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public void get(String key) {
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在读取:");
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object value = maps.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 完成读取:" + value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();
        }
    }
}

输出

1	 正在写入:1
1	 完成写入:
2	 正在写入:2
2	 完成写入:
0	 正在写入:0
0	 完成写入:
4	 正在写入:4
4	 完成写入:
3	 正在写入:3
3	 完成写入:
0	 正在读取:
1	 正在读取:
2	 正在读取:
3	 正在读取:
4	 正在读取:
2	 完成读取:2
1	 完成读取:1
0	 完成读取:0
4	 完成读取:4
3	 完成读取:3

本文作者:李小龙他哥

本文链接:https://www.cnblogs.com/lhnstart/p/16006550.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   李小龙他哥  阅读(59)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑