• 试着简单易懂记录synchronized this object Class的区别,模拟ConcurrentHashMap


    修饰静态方法默认获取当前class的锁,同步方法没有释放的锁,不影响class其他非同步方法的调用,也不影响不同锁的同步方法,更不影响使用class的其他属性.

    package thread.base.sync;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 
     * @author ZhenWeiLai
     *
     */
    public class A {
        
        synchronized public static void saySomething() throws InterruptedException{
            //前后可以写非同步代码
            synchronized(A.class){
                System.out.println("a");
                TimeUnit.SECONDS.sleep(5);
            }
            //前后可以写非同步代码
        }
            
    //        /**
    //         *     与上面相等(但是方法内没有非同步区域):可以这么理解,把synchronized当修饰符用总不能写成synchronized(A.claa) public static void saySomething()
    //         *     这是静态方法啊,不需要实例就可以调用,那么我获取什么锁啊?只能获取本class的锁吖,你又不能给我传参
    //         */
    //        synchronized public static void saySomething() throws InterruptedException{
    //                System.out.println("a");
    //                TimeUnit.SECONDS.sleep(5);
    //        }
    }

    修饰非静态方法,默认获取调用此方法的实例对象锁    Spring容器管理的bean默认都是单例的(当然可以注解为prototype),所以加上 synchronized 的方法,注解自动装配获取的实例,调用都会同步了

    package thread.base.sync;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 
     * @author ZhenWeiLai
     *
     */
    public class A {
    
        public void saySomething() throws InterruptedException {
            //前面可以写非同步代码
            synchronized (this) {
                System.out.println("a");
                TimeUnit.SECONDS.sleep(5);
            }
            //后面可以写非同步代码
        }
        
    //    /**
    //     *     与上面相等,看了静态的,再看对象方法更好理解.
    //     *  必须要有实例才能调用吧?太好办了那么我就获取当前实例对象的锁
    //     */
    //    synchronized public void saySomething() throws InterruptedException{
    //            System.out.println("a");
    //            TimeUnit.SECONDS.sleep(5);
    //    }
    }

    synchronized (object)与synchronized (this)一样,获取实例对象的锁.因为synchronized (this)只能获取当前实例锁,那么synchronized (object)就是可以获取其他实例锁的意思

    对于synchronized 最简单粗暴的理解就是,你要哪些线程方法同步,就跟他们获取一样的锁好了,A.class,就获取A.class,  objectA 就获取 objectA(我说的不是对象名相同,而是真真切切在java堆中的同一个对象),

    据说ConcurrentHashMap 就是用16个对象锁实现的

    我也模拟一下

    package thread.base.sync.hashmap;
    
    import java.util.HashMap;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 
     * @author ZhenWeiLai 模拟16个并发的线程安全put
     * @param <K>
     * @param <V>
     */
    public class SyncHashMap<K, V> extends HashMap<K, V> {
        /**
         * 
         */
        private static final long serialVersionUID = 4071859310703431745L;
    
        // 初始化16个对象锁
        Object[] lock = new Object[16];
    
        public SyncHashMap() {
            for (int i = 0; i < 16; i++) {
                lock[i] = new Object();
            }
        }
    
        /**
         * 线程安全方法
         * 
         * @param key
         * @param value
         * @return
         * @throws InterruptedException
         */
        public V safePut(K key, V value) throws InterruptedException {
            /**
             * 不一样的key有可能得到一样的余数,据说hashcode算法问题.我这里是取巧,不深究
             * 万一两个不同的key得到一样的余数,那么慢的一个就要等待5秒咯
             * 随机获取16个对象锁中的一个
             */
            synchronized (lock[key.hashCode() % 16]) {
                System.out.println("aaaa");
                TimeUnit.SECONDS.sleep(5);
                // 同步调用原生put方法
                return super.put(key, value);
            }
        }
    
    }

    测试方法,两个key相同,其中获取锁慢的那个线程会等5秒再输出控制台

    package thread.base.sync.hashmap;
    
    /**
     * 
     * @author ZhenWeiLai
     *
     */
    public class TestClass {
        public static void main(String[] args) {
            SyncHashMap<Integer,Integer> shm = new SyncHashMap<>();
    new Thread(()->{
                try {
                    //两个线程操作同一个key,
                    shm.safePut(1, 1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            
            new Thread(()->{
                try {
                    //两个线程操作同一个key
                    shm.safePut(1,1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
  • 相关阅读:
    洛谷 P1401 城市(二分+网络流)
    洛谷 P2057 善意的投票(网络流最小割)
    洛谷 P1402 酒店之王
    二分图最大匹配的一些证明
    P2764 最小路径覆盖问题(网络流24题之一)
    洛谷 P2055 [ZJOI2009]假期的宿舍
    P2891 [USACO07OPEN]吃饭Dining(最大流+拆点)
    洛谷P1345 [USACO5.4]奶牛的电信(最小割)
    网络流24题之星际转移问题(洛谷P2754)
    LeetCode Unique Binary Search Trees
  • 原文地址:https://www.cnblogs.com/sweetchildomine/p/6602669.html
Copyright © 2020-2023  润新知