Java并发编程实践笔记(二)——chapter1(线程安全)

1.原子性与线程安全

多个线程操作一个全局变量int i,操作是i++。

1.可见性

通过volatile关键字是解决线程安全问题的必要条件。

2.原子性

保证原子性也是解决线程安全问题的必要条件。
因为i++不是原子操作,读取值,加一和赋值操作是分开的,所以有可能在读取值之后(读取到当前线程副本中),i值(主线程中的本尊)就发生了变化,也有可能i在被赋值之前发生不可预期的变化。

3.举例——为状态组件中使用状态

servlet是无状态的,但是如果需要用无状态的servlet去记录一个状态(比如请求次数),这个时候要么使用同步,要么使用原子变量。

2.状态

“编写线程安全的代码,本质上就是管理对状态的访问”,而且通常是共享的可变的状态。

1.无状态

无状态就不需要注意线程安全,或者不可变的状态,比如String或者一些Immutable的变量。
无状态的对象永远是线程安全的”。

2.同步

对于有状态并且状态可变的情况,使用原子变量或者同步来解决。

3.线程安全的类

线程安全的类是在使用的时候,不需要使用者自己去考虑额外的同步。
比如SynchronizedMap,在内部方法的调用上,封装了一个互斥锁,这个动作是在内部封装好的,使用者不需要自己使用额外的同步操作就可以得到线程安全的保证,这个就是线程安全的类,代码如下:
private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            if (m==null)
                throw new NullPointerException();
            this.m = m;
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        ……
}

4.竞争

检查在运行

check-then-act,检查在运行。
check条件通过的时候,在act的时候,该条件的状态可能已经改变。
举例1:
比如篮子里如果有小于5个苹果,我就向篮子中增加苹果,当我真正向篮子中增加苹果这个动作执行的时候,篮子中可能已经有超过5个苹果了,就会导致新添加的苹果洒出来了)。
举例2:
比如判断在系统的某个路径下,如果不存在一个文件,就去创建该文件。但是真正去创建的这个动作进行的时候,可能该文件已经存在了,这个时候就会造成已有文件被覆盖。

复合操作

给一系列的复合操作加锁,从结果上看相当于使用了原子操作,可能性能会有不同。

多个关联的状态变量

对这多个关联的状态变量的修改,要放在一个原子操作或者放到一个锁里面。

猜你喜欢

转载自blog.csdn.net/xxcupid/article/details/52793068