• java中的锁


    引言

    在java单线程中,并不会出现资源抢夺的现象,但是在多线程并发中,会出现资源抢夺现象。为了避免这种情况需要上锁

    分类

    可重入锁,又名递归锁

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

    使用synchronized

    class Phone{
    
        public void sendSMS() {
            System.out.println(Thread.currentThread().getName()+"	 invoked sendSMS()");
            sendEmail();
        }
    
        private void sendEmail() {
            System.out.println(Thread.currentThread().getName()+"	 ###########invoked 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	 invoked sendSMS()	//t1线程在外层方法获取锁的时候
    t1	 ###########invoked sendEmail()	 //t1在线程进入内层方法会自动获取锁
    t2	 invoked sendSMS()
    t2	 ###########invoked sendEmail()
    

    使用ReentrantLock

    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class Phone implements Runnable {
    
    
        Lock lock = new ReentrantLock();
    
        public void sendSMS() {
            System.out.println(Thread.currentThread().getName() + "	 invoked sendSMS()");
            sendEmail();
        }
    
        private void sendEmail() {
            System.out.println(Thread.currentThread().getName() + "	 ###########invoked sendEmail()");
        }
    
        @Override
        public void run() {
            get();
        }
    
        public void get() {
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + "	 invoked get()");
                set();
            }catch (Exception e){
    
            }finally {
                lock.unlock();
            }
    
        }
    
        private void set() {
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + "	 invoked set()");
            }catch (Exception e){
    
            }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();
    
    
            //暂停一会线程
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println();
            System.out.println();
            System.out.println();
            System.out.println();
            
            Thread t3 = new Thread(phone,"t3");
            Thread t4 = new Thread(phone,"t4");
            t3.start();
            t4.start();
    
        }
    }
    
    

    结果

    t1	 invoked sendSMS()
    t1	 ###########invoked sendEmail()
    t2	 invoked sendSMS()
    t2	 ###########invoked sendEmail()
    
    
    
    
    t3	 invoked get()
    t3	 invoked set()
    t4	 invoked get()
    t4	 invoked set()
    

    自旋锁

    CAS循环比较并交换

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

    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicReference;
    
    public class SpinLockDemo {
    
        AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
        public void myLock() {
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName() + "	 come in...");
            while (!atomicReference.compareAndSet(null, thread)) {
    
            }
    
        }
    
        public void myUnLock() {
    
            Thread thread = Thread.currentThread();
            atomicReference.compareAndSet(thread, null);
            System.out.println(Thread.currentThread().getName() + "	 invoked myUnLock()");
    
        }
    
        public static void main(String[] args) {
            SpinLockDemo spinLockDemo = new SpinLockDemo();
    
            new Thread(() -> {
                spinLockDemo.myLock();
                //暂停一会线程
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinLockDemo.myUnLock();
    
            }, "AA").start();
    
    
            //暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
    
            new Thread(() -> {
                spinLockDemo.myLock();
    
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinLockDemo.myUnLock();
    
            }, "BB").start();
    
        }
    }
    
    

    结果

    AA	 come in...
    BB	 come in...
    AA	 invoked myUnLock()
    BB	 invoked myUnLock()
    

    读写锁

    读时共享资源数据,写时独占资源数据

    多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行,但是如果有一个线程想去写共享资源,就不应该再有其它线程可以对该资源进行读或写。

    读-读能共存

    读-写不能共存

    写-写不能共存

    没有使用读写锁前

    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    class MyCache{
        private volatile Map<String,Object> map=new HashMap<>();
    
    
        public void put(String key, String 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()+"	 写入完成:");
    
        }
    
        public void get(String key) {
            System.out.println(Thread.currentThread().getName()+"	 正在读取:");
            //暂停一会线程
            try {
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName()+"	 读取完成:"+result);
        }
    }
    public class ReadWriteDemo {
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
    
            //创建5个线程,写入资源数据
            for(int i=1;i<=5;i++){
                final  int tempInt = i;
                new Thread(()->{
                    myCache.put(tempInt+"",tempInt+"");
                },String.valueOf(i)).start();
            }
    
    
            //创建5个线程,读取资源数据
            for(int i=1;i<=5;i++){
                final  int tempInt = i;
                new Thread(()->{
                    myCache.get(tempInt+"");
                },String.valueOf(i)).start();
            }
        }
    }
    
    

    结果

    写时并不满足原子性和独占性,整个过程必须是一个完整的统一体,中间不允许被分割,被打断

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

    使用读写锁后

    使用ReentrantReadWriteLock

    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    class MyCache {
        private volatile Map<String, Object> map = new HashMap<>();
    
        //读写锁
        private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
    
    
        public void put(String key, String value) {
    
            rwlock.writeLock().lock();
            try {
    
                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() + "	 写入完成:");
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                rwlock.writeLock().unlock();
            }
    
    
        }
    
        public void get(String key) {
    
            rwlock.readLock().lock();
            try {
    
                System.out.println(Thread.currentThread().getName() + "	 正在读取:");
                //暂停一会线程
                try {
                    TimeUnit.MICROSECONDS.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Object result = map.get(key);
                System.out.println(Thread.currentThread().getName() + "	 读取完成:" + result);
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                rwlock.readLock().unlock();
            }
        }
    }
    
    public class ReadWriteDemo {
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
    
            //创建5个线程,写入资源数据
            for (int i = 1; i <= 5; i++) {
                final int tempInt = i;
                new Thread(() -> {
                    myCache.put(tempInt + "", tempInt + "");
                }, String.valueOf(i)).start();
            }
    
    
            //创建5个线程,读取资源数据
            for (int i = 1; i <= 5; i++) {
                final int tempInt = i;
                new Thread(() -> {
                    myCache.get(tempInt + "");
                }, String.valueOf(i)).start();
            }
        }
    }
    
    
    

    结果

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

    参考

    一道面试题比较synchronized和读写锁 - where - ITeye博客

  • 相关阅读:
    对于JavaScript中this关键字的理解
    使用DOM对表格进行增删
    sql server 存储过程
    sql sever 基础 练习题
    sql sever 基础 建表
    第十章 嵌入式的Linux调试技术
    第九章 硬件抽象层 HAL
    第八章 蜂鸣器驱动
    LED:控制发光二极管
    第一个Linux驱动程序:统计单词个数
  • 原文地址:https://www.cnblogs.com/lisingshen/p/11569310.html
Copyright © 2020-2023  润新知