读《java并发编程》零星笔记

2017-03-19  本文已影响158人  zlcook

java并发编程
书中代码

线程安全

“当多个线程访问某个类时,不管运行时环境采用何种调度方式,或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的”。

内存可见性

深入理解Java虚拟机笔记---原子性、可见性、有序性

重排序

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

深入理解Java内存模型(二)——重排序

正确性

某个类的行为与其规范完全一致。在良好的规范中通常会定义各种不变性条件(Invariant)来约束对象的状态s,以及定义各种后验条件(Postcondition)来描述对象操作的结果。

非原子性的64位操作

内存可见性和原子性:Synchronized和Volatile的比较

Java内存模型要求,变量的读取操作和写入操作必须时原子操作,但对于非volatile类型的long和double变量,JVM允许将64位的读操作和写操作分解为两个32位的操作,当读取一个非volatile类型的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位,因此在多线程中使用共享且可变(某个线程会对该变量执行写操作)的long和double等类型的bain了也是不安全的,除非使用volatile来声明他们或者使用锁保护起来。(也许以后的处理器就都可以提供64位数值的原子操作)

volatile变量

public class CountingSheep {
    volatile boolean asleep;
    void tryToSleep() {
        while (!asleep)
            countSomeSheep();
    }
    void countSomeSheep() {
        // One, two, three...
    }
}
/**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

细说Java多线程之内存可见性-视频

发布与逸出

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        });
    }
    void doSomething(Event e) {
    }
    interface EventSource {
        void registerListener(EventListener e);
    }
    interface EventListener {
        void onEvent(Event e);
    }
    interface Event {
    }
}
public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }
    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
    void doSomething(Event e) {
    }
    interface EventSource {
        void registerListener(EventListener e);
    }
    interface EventListener {
        void onEvent(Event e);
    }
    interface Event {
    }
}

竞态条件与复合操作

public class LazyInitRace {
    private ExpensiveObject instance = null;

    public ExpensiveObject getInstance() {
        if (instance == null)
            instance = new ExpensiveObject();
        return instance;
    }
}

以下代码存在竞态条件,count++包含了”读取-修改-写入”三个操作。

public class UnsafeCountingFactorizer extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        ++count;
        encodeIntoResponse(resp, factors);
    }
....
}

如下代码:使用AtomicLong来代替long类型的计数器,能够确保所有对计数器状态的访问都是原子的,

public class CountingFactorizer extends GenericServlet implements Servlet {
    private final AtomicLong count = new AtomicLong(0);

    public long getCount() { return count.get(); }
    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        count.incrementAndGet();
        encodeIntoResponse(resp, factors);
    }
}

线程封闭

Ad-hoc线程封闭(脆弱,不推荐)

维护线程封闭性的职责完全有程序实现来承担,很脆弱不建议使用。
如下代码通过Map实现线程封闭性:
其中static类型的data是线程间共享的,但是为了实现数据和线程绑定,所以通过map来存放不同线程操作的数据data,data的获取和线程也是绑定的,这样就实现了data数据在单线程中访问了,不会与其它线程共享,注map是和其它线程共享的, 这样每个线程中操作的A、B类都是共享和本线程绑定的那个data,从而不会冲突和出错。

Ad-hoc线程封闭

栈封闭性

public int loadTheArk(Collection<Animal> candidates) {
        SortedSet<Animal> animals;
        int numPairs = 0;
        Animal candidate = null;

        // animals confined to method, don't let them escape!
        animals = new TreeSet<Animal>(new SpeciesGenderComparator());
        animals.addAll(candidates);
        for (Animal a : animals) {
            if (candidate == null || !candidate.isPotentialMate(a))
                candidate = a;
            else {
                ark.load(new AnimalPair(candidate, a));
                ++numPairs;
                candidate = null;
            }
        }
        return numPairs;
    }

ThreadLocal类(重点)

public class ConnectionDispenser {
    static String DB_URL = "jdbc:mysql://localhost/mydatabase";

    private ThreadLocal<Connection> connectionHolder
            = new ThreadLocal<Connection>() {
                public Connection initialValue() {
                    try {
                        return DriverManager.getConnection(DB_URL);
                    } catch (SQLException e) {
                        throw new RuntimeException("Unable to acquire Connection, e");
                    }
                };
            };

    public Connection getConnection() {
        return connectionHolder.get();
    }
}

线程封闭性、线程内数据共享

Paste_Image.png

不变性

public final class ThreeStooges {
    private final Set<String> stooges = new HashSet<String>();

    public ThreeStooges() {
        stooges.add("Moe");
        stooges.add("Larry");
        stooges.add("Curly");
    }
    public boolean isStooge(String name) {
        return stooges.contains(name);
    }
    public String getStoogeNames() {
        List<String> stooges = new Vector<String>();
        stooges.add("Moe");
        stooges.add("Larry");
        stooges.add("Curly");
        return stooges.toString();
    }
}

Final域

安全发布

不正确的发布:正确的对象被破坏

final域的内存语义

不可变对象与初始化安全性

安全发布的常用模式

详解Java中的clone方法 -- 原型模式
string 在clone()中的特殊性 (转载)

--------待更新

上一篇 下一篇

猜你喜欢

热点阅读