开发者社区> 问答> 正文

java无锁并发 报错

"

下面代码里无锁和有锁比是更好的实现吗?我用jmeter每秒20个请求,无锁代码执行test()里的sleep操作的输出大部分与500毫秒差别巨大,而有锁代码的输出基本就是500毫秒相差1,2毫秒的样子,这个问题很怪异啊....

@Controller @RequestMapping("/bench/") public class BenchController {

@Autowired
private FlowService flowService;

private static Object[] lockObj;
private static AtomicReference<Integer>[] locks;

static {
    lockObj = new Object[100];
    for (int i = 0; i < lockObj.length; i++) {
        lockObj[i] = new Object();
    }

    locks = new AtomicReference[100];
    for (int i = 0; i < locks.length; i++) {
        locks[i] = new AtomicReference<Integer>(null);
    }
}

@RequestMapping("a")
@ResponseBody
public long a(int id) throws Exception {
    long start = System.currentTimeMillis();
    int index = id % 100;
    long inner=0;
    synchronized (lockObj[index]) {
        inner=test();
    }
    long result = System.currentTimeMillis() - start;
    System.out.println("all: "+result+" inner: "+inner);
    return result;
}

@RequestMapping("b")
@ResponseBody
public long b(int id) throws Exception {
    long start = System.currentTimeMillis();
    AtomicReference<Integer> lock=locks[id % 100];
    while (!lock.compareAndSet(null, id)) {}
    long inner=test();
    boolean flag=lock.compareAndSet(id, null);
    long result = System.currentTimeMillis() - start;
    System.out.println("all: "+result+" inner: "+inner+" flag:"+flag);
    return result;
}

public long test()throws Exception{
    long innerstart = System.currentTimeMillis();
    Thread.sleep(500);
    System.out.println(System.currentTimeMillis()-innerstart);
    return System.currentTimeMillis()-innerstart;
}

}

"

展开
收起
因为相信,所以看见。 2020-05-27 10:04:06 2962 0
1 条回答
写回答
取消 提交回答
  • 阿里,我所有的向往

    "

    1.首先,明确两个问题,synchronized 一般不是跟AtomicXX类进行比较,更多的是跟ReentrantLock这个类进行比较,网上关于这2者的比较很多,可以自行google之。

    2.问题中关于无锁跟有锁的疑问,测试代码b中的代码是有问题的,

    • 对于方法a,synchronized代码块来说,锁被第一个进来的线程持有后,后续线程请求获取锁会被阻塞挂起,直到前面一个线程释放锁,后续的线程会恢复执行,由于锁的存在,20个请求类似于顺序执行,这一层由jvm调度

    • 对于方法b,cas操作是非阻塞的,方法中的while循环其实是一直在执行(不断尝试进行cas操作),而我们知道,死循环是会消耗cpu资源的,并发数越多,线程越多,此处的cas操作越多,必然导致cpu使用率飙升,方法b中的代码由jmeter测试的时候理论上来说应该一直由20个活跃的工作线程存在,cpu与线程模型是另外一个话题,线程数的调优是jvm一个比较高级的话题,感兴趣可以自行google之

    • 说说ReentrantLock与synchronized:通常情况下在高并发下,ReentrantLock比synchronized拥有更好的性能,而且ReentrantLock提供来一些synchronized并不提供的功能(锁超时自动放弃等),示例代码中可以减少sleep的时间,从而模拟更短停顿,更高的并发,500ms对于人来说很短,对于cpu来说基本就是天文数字了,基本用“慢如蜗牛”来形容也不为过,修改类一下示例代码:

    package com.gzs.learn.springboot;
    
    import java.util.LinkedList;
    import java.util.Random;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    @RequestMapping("/bench/")
    public class BenchController {
        private Random random = new Random();
        private static Object[] lockObj;
        private static AtomicReference<Integer>[] locks;
        private static ReentrantLock[] reentrantLocks;
        static {
            lockObj = new Object[100];
            for (int i = 0; i < lockObj.length; i++) {
                lockObj[i] = new Object();
            }
    
            locks = new AtomicReference[100];
            for (int i = 0; i < locks.length; i++) {
                locks[i] = new AtomicReference<Integer>(null);
            }
            reentrantLocks = new ReentrantLock[100];
            for (int i = 0; i < reentrantLocks.length; i++) {
                reentrantLocks[i] = new ReentrantLock();
            }
        }
    
        @RequestMapping("a/{id}")
        @ResponseBody
        public long a(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            int index = id % 100;
            long inner = 0;
            synchronized (lockObj[index]) {
                inner = test();
            }
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner);
            return result;
        }
    
        @RequestMapping("b/{id}")
        @ResponseBody
        public long b(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            id = id % 100;
            AtomicReference<Integer> lock = locks[id];
            int b = 0;
            while (!lock.compareAndSet(null, id)) {
                b = 1 + 1;
            }
            long inner = test();
            boolean flag = lock.compareAndSet(id, null);
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner + " flag:" + flag);
            System.out.println(b);
            return result;
        }
    
        @RequestMapping("c/{id}")
        @ResponseBody
        public long c(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            id = id % 100;
            ReentrantLock lock = reentrantLocks[id];
            lock.lock();
            long inner = test();
            lock.unlock();
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner);
            return result;
        }
    
        public long test() throws Exception {
            long innerstart = System.currentTimeMillis();
            Thread.sleep(0, 100);
            // Thread.sleep(500);
            System.out.println(System.currentTimeMillis() - innerstart);
            return System.currentTimeMillis() - innerstart;
        }
    }
    
    • 方法c是用ReentrantLock实现的,绝大多少情况下ReentrantLock比synchronized高效

    • juc(java.util.concurrent)中的核心类Aqs(AbstractQueuedSynchronizer)是一个基于队列的 并发包,默认线程在锁竞争(自旋)超过1000纳秒的时候会被park(挂起操作),从而减少cpu频繁的线程切换,可以尝试调整方法c中的sleep的时间参数。

    • 测试方法,本机没有装jmeter,用apache ab做的测试,测试命令:

    ab -n 100 -c 20  http://localhost:8080/bench/a/10
    "
    2020-05-27 17:33:13
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Cloud Alibaba - 重新定义 Java Cloud-Native 立即下载
The Reactive Cloud Native Arch 立即下载
JAVA开发手册1.5.0 立即下载