• Java 中的锁如何使用?有什么注意事项?


    Java 中常见的锁有

    • synchronized
    • 可重入锁 java.util.concurrent.lock.ReentrantLock
    • 可重复读写锁 java.util.concurrent.lock.ReentrantReadWriteLock

    synchronized 有 3种用法

    • 修饰普通方法,执行方法代码,需要获取对象本身 this 的锁
    package constxiong.concurrency.a18;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 测试 synchronized 普通方法
     * @author ConstXiong
     * @date 2019-09-19 10:49:46
     */
    public class TestSynchronizedNormalMethod {
    
        private int count = 0;
        
    //    private void add1000() {
        private synchronized void add1000() { //使用 synchronized 修饰 add100 方法,即可获得正确的值 30000
            for (int i = 0; i < 1000; i++) {
                count++;
            }
        }
        
        //启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
        private void test() throws InterruptedException {
            List<Thread> threads = new ArrayList<Thread>(10);
            
            for (int i = 0; i < 30; i++) {
                Thread t =  new Thread(() -> {
                    add1000();
                });
                t.start();
                threads.add(t);
            }
            
            //等待所有线程执行完毕
            for (Thread t : threads) {
                t.join();
            }
            
            //打印 count 的值
            System.out.println(count);
        }
        
        public static void main(String[] args) throws InterruptedException {
            //创建 TestSynchronizedNormalMethod 对象,调用 test 方法
            new TestSynchronizedNormalMethod().test();
        }
    }
    • 修饰静态方法,执行方法代码,需要获取 class 对象的锁
    package constxiong.concurrency.a18;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 测试 synchronized 静态方法
     * @author ConstXiong
     * @date 2019-09-19 10:49:46
     */
    public class TestSynchronizedStaticMethod {
    
        private static int count = 0;
        
        private static void add1000() {
    //    private synchronized static void add1000() { //使用 synchronized 修饰 add100 方法,即可获得正确的值 30000
            for (int i = 0; i < 1000; i++) {
                count++;
            }
        }
        
        public static void main(String[] args) throws InterruptedException {
            
            //启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
            List<Thread> threads = new ArrayList<Thread>(10);
            
            for (int i = 0; i < 30; i++) {
                Thread t =  new Thread(() -> {
                    add1000();
                });
                t.start();
                threads.add(t);
            }
            
            //等待所有线程执行完毕
            for (Thread t : threads) {
                t.join();
            }
            
            //打印 count 的值
            System.out.println(count);
        }
    }
    • 锁定 Java 对象,修饰代码块,显示指定需要获取的 Java 对象锁
    package constxiong.concurrency.a18;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 测试 synchronized 代码块
     * @author ConstXiong
     * @date 2019-09-19 10:49:46
     */
    public class TestSynchronizedCodeBlock {
    
        private int count = 0;
        
        //锁定的对象
        private final Object obj = new Object();
        
        private void add1000() {
            
            //执行下面的加 1000 的操作,都需要获取 obj 这个对象的锁
            synchronized (obj) {
                for (int i = 0; i < 1000; i++) {
                    count++;
                }
            }
        }
        
        //启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
        private void test() throws InterruptedException {
            List<Thread> threads = new ArrayList<Thread>(10);
            
            for (int i = 0; i < 30; i++) {
                Thread t =  new Thread(() -> {
                    add1000();
                });
                t.start();
                threads.add(t);
            }
            
            //等待所有线程执行完毕
            for (Thread t : threads) {
                t.join();
            }
            
            //打印 count 的值
            System.out.println(count);
        }
        
        public static void main(String[] args) throws InterruptedException {
            //创建 TestSynchronizedNormalMethod 对象,调用 test 方法
            new TestSynchronizedCodeBlock().test();
        }
    }

    可重入锁 java.util.concurrent.lock.ReentrantLock 的使用示例

    package constxiong.concurrency.a18;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 测试 ReentrantLock 
     * @author ConstXiong
     * @date 2019-09-19 11:26:50
     */
    public class TestReentrantLock {
    
        private int count = 0;
        
        private final Lock lock = new ReentrantLock();
        
        private void add1000() {
            lock.lock();
            try {
                for (int i = 0; i < 1000; i++) {
                    count++;
                }
            } finally {
                lock.unlock();
            }
        }
        
        //启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
        private void test() throws InterruptedException {
            List<Thread> threads = new ArrayList<Thread>(10);
            
            for (int i = 0; i < 30; i++) {
                Thread t =  new Thread(() -> {
                    add1000();
                });
                t.start();
                threads.add(t);
            }
            
            //等待所有线程执行完毕
            for (Thread t : threads) {
                t.join();
            }
            
            //打印 count 的值
            System.out.println(count);
        }
        
        public static void main(String[] args) throws InterruptedException {
            //创建 TestReentrantLock 对象,调用 test 方法
            new TestReentrantLock().test();
        }
        
    }

     

    可重复读写锁 java.util.concurrent.lock.ReentrantReadWriteLock 的使用示例

    package constxiong.concurrency.a18;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
     
    /**
     * 测试可重入读写锁 ReentrantReadWriteLock
     * @author ConstXiong
     * @date 2019-09-19 11:36:19
     */
    public class TestReentrantReadWriteLock {
        
        //存储 key value 的 map
        private Map<String, Object> map = new HashMap<String, Object>();
        
        //读写锁
        private final ReadWriteLock lock = new ReentrantReadWriteLock();
        
        /**
         * 根据 key 获取 value
         * @param key
         */
        public Object get(String key) {
            Object value = null;
            lock.readLock().lock();
            try {
                Thread.sleep(50L);
                value = map.get(key);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.readLock().unlock();
            }
            return value; 
        }
        
        /**
         * 设置key-value
         * @param key
         */
        public void set(String key, Object value) {
            lock.writeLock().lock();
            try {
                Thread.sleep(50L);
                map.put(key, value);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.writeLock().unlock();
            }
        }
     
        //测试5个线程读数据,5个线程写数据
        public static void main(String[] args) {
            //创建测试可重入读写锁 TestReentrantReadWriteLock 对象
            TestReentrantReadWriteLock test = new TestReentrantReadWriteLock();
            
            String key = "lock";//存入 map 中的 key
            Random r = new Random();//生成随机数作为 value
            
            for (int i = 0; i < 5; i++) {
                //5 个线程读 map 中 key 的 value
                new Thread(() -> {
                    for (int j = 0; j < 10; j++) {
                        System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));
                    }
                }).start();
                
                //5 个线程写 map 中 key 的 value
                new Thread(() -> {
                    for (int j = 0; j < 10; j++) {
                        int value = r.nextInt(1000);
                        test.set(key, value);
                        System.out.println(Thread.currentThread().getName() + " write value=" + value);
                    }
                }).start();
            }
        }
        
    }

    锁的使用注意事项

    • synchronized 修饰代码块时,最好不要锁定基本类型的包装类,如 jvm 会缓存 -128 ~ 127 Integer 对象,每次向如下方式定义 Integer 对象,会获得同一个 Integer,如果不同地方锁定,可能会导致诡异的性能问题或者死锁
    Integer i = 100; 
    • synchronized 修饰代码块时,要线程互斥地执行代码块,需要确保锁定的是同一个对象,这点往往在实际编程中会被忽视
    • synchronized  不支持尝试获取锁、锁超时和公平锁
    • ReentrantLock 一定要记得在 finally{} 语句块中调用 unlock() 方法释放锁,不然可能导致死锁
    • ReentrantLock 在并发量很高的情况,由于自旋很消耗 CPU 资源
    • ReentrantReadWriteLock 适合对共享资源写操作很少,读操作频繁的场景;可以从写锁降级到读锁,无法从读锁升级到写锁

     来一道刷了进BAT的面试题?

  • 相关阅读:
    tar 命令详解
    必问的Java集合框架面试题
    计算机网络面试总结
    Java并发面试题
    error: cannot lock ref 'refs/remotes/origin/master': unable to resolve reference 'refs/remotes/origin/master': reference broken...
    String 类型的值能够被反射改变从而引发的意外事件
    dir 命令手册
    JVM可达性分析算法中,哪些可以作为 root ?
    Resnest:注意力+分组卷积的融合
    mmdetection(一)安装及训练、测试VOC格式的数据
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11687790.html
Copyright © 2020-2023  润新知