一次代码的优化经历

原始代码

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 实现方法 int count(String ip) 返回ip访问次数
 */
public class IpCount {
    
    
    static volatile ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
    static final Object o = new Object();

    public static int count(String ip) {
    
    
        if (count.keySet().contains(ip)) {
    
    
            return count.get(ip).incrementAndGet();
        }
        synchronized (o) {
    
    
            if (count.keySet().contains(ip)) {
    
    
                return count.get(ip).incrementAndGet();
            } else {
    
    
                count.put(ip, new AtomicInteger(1));
                return 1;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread[] threads = new Thread[10000];
        Random r = new Random();
        IpCount ipCount = new IpCount();
        for (int i = 0; i < threads.length; i++) {
    
    
            threads[i] = new Thread(() -> {
    
    
                int ip = r.nextInt(5)+1;
                int a = ipCount.count(Integer.toString(ip));
            }, "t" + i);
        }
        for (Thread t : threads) {
    
    
            t.start();
        }
        Thread.sleep(5);
        System.out.println(ipCount.count);
        int sum  = 0;
        for (AtomicInteger value : ipCount.count.values()) {
    
    
            sum +=value.get();
        }
        System.out.println(sum);
    }
}

运行结果

在这里插入图片描述

  • 思路分析
  • 首先看到这些代码时,synchronized与Atomic同时使用
  • 也就是说,锁升级与CAS同时使用
  • Volatile的作用是 防止指令重排序和线程可见性
  • ConcurrentHashMap当调用时不存在出现半初始话状态
  • 这种极短的操作使用原子操作确实好很多,第一想法是synchronized去掉
  • 静态方法也没必要

去掉之后

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 实现方法 int count(String ip) 返回ip访问次数
 */
public class IpCount {
    
    
    ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
    public int count(String ip) {
    
    
        if (count.keySet().contains(ip)) {
    
    
            return count.get(ip).incrementAndGet();
        } else {
    
    
            count.put(ip, new AtomicInteger(1));
            return 1;
        }
    }
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread[] threads = new Thread[10000];
        Random r = new Random();
        IpCount ipCount = new IpCount();
        for (int i = 0; i < threads.length; i++) {
    
    
            threads[i] = new Thread(() -> {
    
    
                int ip = r.nextInt(5)+1;
                int a = ipCount.count(Integer.toString(ip));
            }, "t" + i);
        }
        for (Thread t : threads) {
    
    
            t.start();
        }
        Thread.sleep(5);
        System.out.println(ipCount.count);
        int sum  = 0;
        for (AtomicInteger value : ipCount.count.values()) {
    
    
            sum +=value.get();
        }
        System.out.println(sum);
    }
}

在这里插入图片描述
确实是出了一些问题,在意料之中

  • 到这里其实出现了沉思ConcurrentHashMap本身是线程安全的,为什么contains()会出现不安全了
  • 突然发现count.keySet().contains(ip)多出了一个keySet()
  • 尝试用count.containsKey(ip)替换count.keySet().contains(ip),也不正常
ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
    public int count(String ip) {
    
    
        if (count.containsKey(ip)) {
    
    
            return count.get(ip).incrementAndGet();
        } else {
    
    
            count.put(ip, new AtomicInteger(1));
            return 1;
        }
    }

在这里插入图片描述

putIfAbsent()

但如果使用count.putIfAbsent()还会更优

  • 如何没有key对应的值,put
  • 如果已存在值依旧是原来的值,不做修改
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 实现方法 int count(String ip) 返回ip访问次数
 */
public class IpCount {
    
    
    ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
    public int count(String ip) {
    
    
        count.putIfAbsent(ip, new AtomicInteger(0));
        return count.get(ip).incrementAndGet();
    }
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread[] threads = new Thread[10000];
        Random r = new Random();
        IpCount ipCount = new IpCount();
        for (int i = 0; i < threads.length; i++) {
    
    
            threads[i] = new Thread(() -> {
    
    
                int ip = r.nextInt(5)+1;
                int a = ipCount.count(Integer.toString(ip));
            }, "t" + i);
        }
        for (Thread t : threads) {
    
    
            t.start();
        }
        Thread.sleep(5);
        System.out.println(ipCount.count);
        int sum  = 0;
        for (AtomicInteger value : ipCount.count.values()) {
    
    
            sum +=value.get();
        }
        System.out.println(sum);
    }
}

猜你喜欢

转载自blog.csdn.net/yuell102/article/details/108436200