天天看点

Java并发编程实践阅读笔记

1. 在LazyInitialization中的竞争条件

@NotThreadSafe
public class LazyInitRace {
    private ExpensiveObject instance = null;
    public ExpensiveObject getInstance() {
        if (instance == null)
            instance = new ExpensiveObject();
        return instance;
    } 
}
           

可能发生的情况就是:在多个线程上调用getInstance方法时,产生多个对象

2. 内部锁是基于每线程的,而不是每调用,意味着线程进入了一个synchronize块,它是可以重入这个synchronize块的,每一次重入,内部锁的计数会加一

3. 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;
    } 
}
           

虽然看起来会输出42,但事实上,它很有可能打印出0,或者根本就不会终止

3. 对于非volatile的64位数值变量double和long,JVM允许将他们分为两个32位的操作,除非声明为volatile或者使用锁,volatile变量不会缓存在寄存器或者缓存在对其他处理器隐藏的地方

4. 锁不仅仅是关于互斥和同步的,也是关于内存可见的

5. 对于InterruptedException的处理,可以有两种情况:

(1)外层代码可以处理这个异常,直接抛出这个异常即可

(2)如果不能抛出这个异常,比如在run()方法内,因为在得到这个异常的同时,线程的中断状态已经被清除了,需要保留线程的中断状态,则需要调用Thread.currentThread().interrupt()

6.  如果要在你的程序中实现一个生产者-消费者的设计,使用Executor通常是最简单的方式,

比如下面这个例子:

class TaskExecutionWebServer {
    private static final int NTHREADS = 100;
    private static final Executor exec
        = Executors.newFixedThreadPool(NTHREADS);
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        while (true) {
    	    final Socket connection = socket.accept();
    	    Runnable task = new Runnable() {
            	public void run() {
            	    handleRequest(connection);
	    	} 
	    };
    	    exec.execute(task);
	}
    }
}
           

7. 应该避免使用Thread.stop和suspend方法来强迫线程停止手头的工作,应该使用中断机制

8. 当线程在并不处于阻塞状态的情况下发生中断时,会设置线程的中断状态,然后一直等到被取消的活动获取中断状态,来检查是否发生了中断,如果不触发InterruptedException,中断状态会一直保持,直到有人特意去清除中断状态。

有很多试图写出多线程GUI框架的努力,最终都由于竞争条件和死锁导致的稳定性问题,又回到了单线程化的时间队列模型的老路上来。