@Slf4j
public class InterestingDemo {
        volatile int a = 1;
        volatile int b = 1;
        public synchronized void add() {
            log.info("add start");
            for (int i = 0; i < 10000; i++) {
                a++;
                b++;
            }
            log.info("add done");
        }
        public  void compare() {
            log.info("compare start");
            for (int i = 0; i < 10000; i++) {
                //a始终等于b吗?
                if (a < b) {
                    log.info("a:{},b:{},{}", a, b, a > b);
                    //最后的a>b应该始终是false吗?
                }
            }
            log.info("compare done");
        }
    public static void main(String[] args) {
        InterestingDemo interestingDemo = new InterestingDemo();
        new Thread(() -> interestingDemo.add()).start();
        new Thread(() -> interestingDemo.compare()).start();
    }
    
}
执行结果:
19:50:24.767 [Thread-1] INFO com.cy.lock.InterestingDemo - compare start
19:50:24.767 [Thread-0] INFO com.cy.lock.InterestingDemo - add start
19:50:24.767 [Thread-0] INFO com.cy.lock.InterestingDemo - add done
19:50:24.767 [Thread-1] INFO com.cy.lock.InterestingDemo - a:95,b:353,true
19:50:24.767 [Thread-1] INFO com.cy.lock.InterestingDemo - compare done
从代码的执行结果来看,这段代码显然没有按照我们期望的那些去执行,其中的原因就是我们只对add()加了锁,而没有对compare方法进行加锁。导致这两个方法并不能完全同步执行从而出错,其解决的方法也比较简单,只要再给compare()方法也加上锁就好了
处理锁的逻辑与业务逻辑不一致可能会导致锁失效以外,锁的层级与被保护对象的层级不一致也有可能导致锁的失效
public class Data {
    private static int counter = 0;
    //private static Object locker = new Object();
    public static int reset() {
        counter = 0;
        return counter;
    }
    public synchronized void wrong() {
        counter ++;
    }
    public static int getCounter() {
        return counter;
    }
    public static void main(String[] args) {
        Data.reset();
        IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
            new Data().wrong();
        });
        System.out.println(Data.getCounter());
    }
}
执行结果:
8548
Process finished with exit code 0
从执行结果来看,这个结果与我们所期望的10000不一致,说明程序运行期间出现了线程安全的问题,而这个问题的主要原因就是本来需要用锁保护的静态变量counter属于类,要采用类级别的锁才能保护,而非静态的getCounter方法时属于实例,在改方法上的锁是实例级别的锁,并不能保护静态变量counter,对于这个问题,一方面可以通过将getCounter()改成静态的方法然后再加锁,或者是直接对静态变量加锁来解决。
锁是有不同的粗粒度的,锁的粗粒度过大必然会影响到性能,锁的粗粒度过小又有可能保护不到想要保护的资源,在使用锁的时候也要考虑要选用什么样的颗粒度的锁。
@Slf4j
public class LockLevel {
    private List<Integer> data = new ArrayList<>();
    // 模拟一个慢方法
    private void slow() {
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
        }
    }
    public  int wrong() {
        long begin = System.currentTimeMillis();
        IntStream.rangeClosed(1, 1000).parallel().forEach( i -> {
            synchronized (this) {
                slow();
                data.add(i);
            }
        });
        log.info("took:{}", System.currentTimeMillis() - begin);
        return data.size();
    }
    public int right() {
        long begin = System.currentTimeMillis();
        IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
            slow();
            synchronized (data) {
                data.add(i);
            }
        });
        log.info("took:{}", System.currentTimeMillis() - begin);
        return data.size();
    }
    public static void main(String[] args) {
        LockLevel lockLevel = new LockLevel();
        System.out.println("wrong" + lockLevel.wrong());
        System.out.println("right" + lockLevel.right());
    }
}
执行结果
20:14:15.169 [main] INFO com.cy.lock.LockLevel - took:12120
wrong1000
20:14:18.140 [main] INFO com.cy.lock.LockLevel - took:2960
right2000
Process finished with exit code 0
由结果可以看出,采用不同粗粒度锁的方法执行效率有着非常大的差异,其区别就是一个把慢方法放到了加锁的范围内,一个放在了锁的范围外。因此选择锁的时候也要考虑具体的使用场景,选择合适的锁来尽量减少对效率的影响。
@RestController
@RequestMapping("deadlock")
@Slf4j
public class DeadLockController {
    static class Item {
        final String name;
        int remaining = 1000;
        public Item(String name) {
            this.name = name;
        }
        ReentrantLock lock = new ReentrantLock();
        public String getName() {
            return name;
        }
    }
    private ConcurrentHashMap<String, Item> items = new ConcurrentHashMap<>();
    public DeadLockController() {
        IntStream.range(0, 10).forEach(i -> items.put("item" + i, new Item("item" + i)));
    }
    private List<Item> createCart() {
        return IntStream.rangeClosed(1, 3)
                .mapToObj(i -> "item" + ThreadLocalRandom.current().nextInt(items.size()))
                .map(name -> items.get(name)).collect(Collectors.toList());
    }
    private boolean createOrder(List<Item> order) {
        List<ReentrantLock> locks = new ArrayList<>();
        for (Item item : order) {
            try {
                if (item.lock.tryLock(10, TimeUnit.SECONDS)) {
                    locks.add(item.lock);
                } else {
                    locks.forEach(ReentrantLock::unlock);
                    return false;
                }
            } catch (InterruptedException e) {
            }
        }
        try {
            order.forEach(item -> item.remaining --);
        } finally {
            locks.forEach(ReentrantLock::unlock);
        }
        return true;
    }
    @GetMapping("/wrong")
    public long wrong() {
        long begin = System.currentTimeMillis();
        long success = IntStream.rangeClosed(1, 1000).parallel()
                .mapToObj(i -> {
                    List<Item> cart = createCart().stream()
                            .sorted(Comparator.comparing(Item::getName))
                            .collect(Collectors.toList());
                    return createOrder(cart);
                })
                .filter(result -> result)
                .count();
        log.info("success:{} totalRemaining:{} took:{}ms items:{}",
                success,
                items.entrySet().stream().map(item -> item.getValue().remaining).reduce(0, Integer::sum),
                System.currentTimeMillis() - begin, items);
        return success;
    }
}
原文:https://www.cnblogs.com/cy1995/p/13276002.html