美文网首页Java高级架构程序员java进阶干货
《java编程思想》读书笔记——比较synchroinzed和L

《java编程思想》读书笔记——比较synchroinzed和L

作者: 小小浪把_Dont_know拍 | 来源:发表于2018-01-04 09:24 被阅读193次

在并发的场景下,很容易出现线程安全的问题。使用synchronized可以很方便地解决此问题。

public class EvenGenerator extends IntGenerator {
    private int currentEvenValue = 0;
    @Override
    public int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }

    public static void main(String[] args) {
        EvenChecker.test(new EvenGenerator());
    }
} /* Output: 
Press Control-C to exit
595 not even!
597 not even!
*/

EvenGenerator在并发执行的时候,由于

  1. 是对同一个实例的成员变量操作;
  2. ++currentEvenValue是非原子操作(在之前的文章有讲到:java的同步语法volatile和synchronized);ps:这一点不必要,即使换成了AtomicInteger进行自增,也有线程安全问题;
  3. next方法里涉及多步操作;
    第三点非常重要,很容易出现一个线程将另一个线程的currentEvenValue覆盖或者进行了额外的自增操作,导致得到不符合预期的结果。

synchronized

用synchronized就可以解决上面的问题

public class SynchronizedEvenGenerator extends IntGenerator {
    private int currentEvenValue = 0;

    @Override
    public synchronized int next() {
        ++currentEvenValue;
        Thread.yield();
        ++currentEvenValue;
        return currentEvenValue;
    }

    public static void main(String[] args) {
        EvenChecker.test(new SynchronizedEvenGenerator());
    }
}

为了使并发的情况更容易出现,特意加了一行Thread.yield()调用,线程执行到这行的时候,会将控制权让出去(php的yield相关:zan框架入门(一)——协程)。
之前的文章里讲过synchronized,这里不再赘述。

Lock对象

Lock是java的并发包里提供的功能,并非原生支持。
ReentrantLock是比较常用的Lock类,基于它同样可以解决并发安全问题,虽然逻辑上要比synchronized复杂一些:

public class MutexEvenGenerator extends IntGenerator {
    private int currentEvenValue = 0;
    private Lock lock = new ReentrantLock();
    @Override
    public int next() {
        lock.lock();
        try {
            ++currentEvenValue;
            Thread.yield();
            ++currentEvenValue;
            return currentEvenValue;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        EvenChecker.test(new MutexEvenGenerator());
    }
}

注意return必须在unlock之前调用,否则仍然会有并发安全问题。
可以看到,虽然用Lock需要更多的代码,但是更加灵活,适用于特殊的定制化场景。
一般的,如果可以用synchronized解决的,就直接使用synchronized。某些特殊的场景,比如并发逻辑块抛出异常要进行清理,或者持有锁一段时间后再释放。

相关文章

网友评论

    本文标题:《java编程思想》读书笔记——比较synchroinzed和L

    本文链接:https://www.haomeiwen.com/subject/dhmknxtx.html