zoukankan      html  css  js  c++  java
  • iv004-java锁

    1.公平锁和非公平锁

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

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

    关于两者的区别:

    • 公平锁:公平锁就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。
    • 非公平锁:非公平锁比较粗糙,上来就直接尝试占有锁,如果尝试失败,就再采取类似公平锁那种方式。

    Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的有点在于吞吐量比公平锁大。
    对于Synchronized而言,也是一种非公平锁。

    2.可重入锁(又名递归锁)

    指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程的外层方法获取锁的时候,在进入内层方法会自动获取锁,也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
    可重入锁最大的作用是避免死锁

    • 2.1.synchronized就是一个典型的可重入锁
      示例代码:
    class Phone {
    
        public synchronized void sendSMS() {
    
            System.out.println(Thread.currentThread().getName() + "	sendSMS()");
    
            sendEmail();
        }
    
        public synchronized void sendEmail() {
            System.out.println(Thread.currentThread().getName() + "	sendEmail()");
        }
    }
    
    public class ReentrantLockDemo {
    
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            new Thread(() -> {
                phone.sendSMS();
            }, "t1").start();
    
            new Thread(() -> {
                phone.sendSMS();
            }, "t2").start();
        }
    }
    

    运行结果:

    t1	sendSMS()   //t1线程在外层方法获取锁的时候
    t1	sendEmail()  //t1在进入内存方法会自动获取锁
    t2	sendSMS()
    t2	sendEmail()
    
    • 2.2ReentrantLock也是典型可重入锁
      示例代码:
    class Phone {
    
        Lock lock = new ReentrantLock();
    
        public void sendSMS() {
            lock.lock();
            try {
    
                System.out.println(Thread.currentThread().getName() + "	sendSMS()");
    
                sendEmail();
    
            } finally {
                lock.unlock();
            }
    
    
        }
    
        public void sendEmail() {
            lock.lock();
            try {
    
                System.out.println(Thread.currentThread().getName() + "	sendEmail()");
    
            } finally {
                lock.unlock();
            }
        }
    }
    
    public class ReentrantLockDemo {
    
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            new Thread(() -> {
                phone.sendSMS();
            }, "t1").start();
    
            new Thread(() -> {
                phone.sendSMS();
            }, "t2").start();
        }
    }
    

    运行结果:

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

    3.自旋锁(spinlock)

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

    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 SpinlockDemo {
    
        private AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
        public void myLock() {
            System.out.println(Thread.currentThread().getName() + "	 try get lock");
    
            while (!atomicReference.compareAndSet(null, Thread.currentThread())) {
    
            }
    
            System.out.println(Thread.currentThread().getName() + "	 get lock");
        }
    
        public void myUnlock() {
            atomicReference.compareAndSet(Thread.currentThread(), null);
    
            System.out.println(Thread.currentThread().getName() + "	 unlock");
        }
    
        public static void main(String[] args) throws InterruptedException {
    
            SpinlockDemo spinlockDemo = new SpinlockDemo();
    
            new Thread(() -> {
    
                spinlockDemo.myLock();
    
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinlockDemo.myUnlock();
    
            }, "AA").start();
    
            TimeUnit.SECONDS.sleep(1);
    
            new Thread(() -> {
    
                spinlockDemo.myLock();
    
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinlockDemo.myUnlock();
    
            }, "BB").start();
    
            TimeUnit.SECONDS.sleep(1);
    
            new Thread(() -> {
    
                spinlockDemo.myLock();
    
                spinlockDemo.myUnlock();
    
            }, "CC").start();
        }
    }
    

    运行结果:

    AA	 try get lock
    AA	 get lock
    BB	 try get lock
    CC	 try get lock
    AA	 unlock
    CC	 get lock
    CC	 unlock
    BB	 get lock
    BB	 unlock
    

    4.独占锁(写锁),共享锁(读锁),互斥锁

    独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁。
    共享锁:指该锁可被多个线程所持有。
    对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
    读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。
    不加锁出现线程安全问题:

    class MyCache {
    
        private volatile Map<String, Object> map = new HashMap<>();
    
        public void set(String key, Object value) {
    
            System.out.println(Thread.currentThread().getName() + "	 正在添加:" + key);
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "	 添加完成:" + key);
        }
    
        public Object get(String key) {
            System.out.println(Thread.currentThread().getName() + "	 正在读取:" + key);
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "	 读取完成:" + result);
            return result;
        }
    }
    
    public class ReadWriteLockDemo {
    
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
    
            //write
            for (int i = 1; i <= 5; i++) {
                final int tmp = i;
                new Thread(() -> {
                    myCache.set(tmp + "", tmp + "");
                }, "t" + i).start();
            }
    
    
            //read
            for (int i = 1; i <= 5; i++) {
                final int tmp = i;
                new Thread(() -> {
                    myCache.get(tmp + "");
                }, "t" + (i + 5)).start();
            }
        }
    }
    

    运行结果:

    t2	 正在添加:2
    t4	 正在添加:4
    t3	 正在添加:3
    t5	 正在添加:5
    t1	 正在添加:1
    t6	 正在读取:1
    t7	 正在读取:2
    t8	 正在读取:3
    t9	 正在读取:4
    t10	 正在读取:5
    t3	 添加完成:3
    t7	 读取完成:2
    t10	 读取完成:5
    t8	 读取完成:null
    t2	 添加完成:2
    t9	 读取完成:null
    t1	 添加完成:1
    t6	 读取完成:null
    t5	 添加完成:5
    t4	 添加完成:4
    

    添加读写锁保证线程安全:

    class MyCache {
    
        private volatile Map<String, Object> map = new HashMap<>();
    
        private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
        public void set(String key, Object value) {
            rwLock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + "	 正在添加:" + key);
    
                TimeUnit.MICROSECONDS.sleep(300);
    
                map.put(key, value);
                System.out.println(Thread.currentThread().getName() + "	 添加完成:" + key);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            } finally {
                rwLock.writeLock().unlock();
            }
    
        }
    
        public Object get(String key) {
            rwLock.readLock().lock();
            Object result = null;
            try {
    
                System.out.println(Thread.currentThread().getName() + "	 正在读取:" + key);
                result = map.get(key);
                System.out.println(Thread.currentThread().getName() + "	 读取完成:" + result);
            } finally {
                rwLock.readLock().unlock();
            }
            return result;
        }
    }
    
    public class ReadWriteLockDemo {
    
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
    
            //write
            for (int i = 1; i <= 5; i++) {
                final int tmp = i;
                new Thread(() -> {
                    myCache.set(tmp + "", tmp + "");
                }, "t" + i).start();
            }
    
    
            //read
            for (int i = 1; i <= 5; i++) {
                final int tmp = i;
                new Thread(() -> {
                    myCache.get(tmp + "");
                }, "t" + i).start();
            }
        }
    }
    

    运行结果:

    t2	 正在添加:2
    t2	 添加完成:2
    t1	 正在添加:1
    t1	 添加完成:1
    t3	 正在添加:3
    t3	 添加完成:3
    t5	 正在添加:5
    t5	 添加完成:5
    t4	 正在添加:4
    t4	 添加完成:4
    t1	 正在读取:1
    t1	 读取完成:1
    t2	 正在读取:2
    t4	 正在读取:4
    t5	 正在读取:5
    t5	 读取完成:5
    t3	 正在读取:3
    t2	 读取完成:2
    t3	 读取完成:3
    t4	 读取完成:4
    

    从运行结果上看,写锁(独占锁)是互斥的,读锁是共享的,在某些场景下,读写锁(ReentrantReadWriteLock)在保证线程安全的前提下比Synchronized和ReentrantLock(读写操作都上锁)的并发性更高。即也是读写分离的思想。

  • 相关阅读:
    jvm内存配置参数
    6 个设计原则分别是什么?每种设计原则体现的设计模式是哪个?
    classloader 结构,是否可以自己定义一个 java.lang.String 类,为什么? 双亲代理机制。
    求sum=1+111+1111+........+1....111 .
    交换排序
    字符串压缩 stringZip
    TCP为何采用三次握手来建立连接,若采用二次握手可以吗
    使用jsoup抓取新闻信息
    通过地址获得经纬度(百度Geocoding API)
    FileReader
  • 原文地址:https://www.cnblogs.com/everyingo/p/14554231.html
Copyright © 2011-2022 走看看