程序员

《七周七并发模型》阅读笔记(一)

2016-04-03  本文已影响0人  程序熊大

一、线程与锁——第一天

线程与锁模型其实是对底层硬件运行过程的形式化,这种形式化既是该模型最大的优点,也是它最大的缺点。我们借助Java语言来学习线程与锁模型,不过内容也适用于其他语言。

1、知识点

线程与锁模型会带来三个主要的危害:竞态条件、死锁和内存可见性,本节提供了一些避免这些危害的准则:

2、自习

二、线程与锁——第二天

内置锁虽然方便、灵活,但是也有很多限制:

Java 5之前,常常使用ReentrantLock锁代替synchronized关键字,因为ReentranLock锁可中断、可设置获取锁的超时时间、可实现细粒度加锁(链表上的交替锁)、可使用条件变量。

ReentrantLock的使用模式如下:

Lock lock = new ReentrantLock();
lock.lock();
try {
    《使用共享资源》
} finally {
      lock.unlock();
}

并发编程有时需要等待某个事件发生,条件变量就是为这种情况而生的。使用条件变量的模式是:

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

lock.lock();
try {
      while(!《条件为真》) {
         condition.await();
      }
    《使用共享资源》
} finally {
      lock.unlock();
}

1、知识点

ReentrantLock和java.util.concurrent.atomic突破了使用内置锁的限制,利用新的工具我们可以做到:

2、自习

public ReentrantLock(boolean fair)
//Creates an instance of ReentrantLock with the given fairness policy.
//**Parameters:**
//fair - true if this lock should use a fair ordering policy

如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,也就是说等待时间最长的线程最有机会获取锁,也可以说锁的获取是有序的;反之,则是非公平锁。
公平锁的性能不如非公平锁——公平的获取锁没有考虑到操作系统对线程的调度因素,这样造成JVM对于等待中的线程调度次序和操作系统对线程的调度之间的不匹配;另一方面,公平锁可以防止“饥饿”情况的产生,在以TPS为唯一指标的场景下,可以考虑使用公平锁。

public class MyWaitNotify2{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      if(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

(3)为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁(校注:这种做法要慎重,目前的JVM实现自旋会消耗CPU,如果长时间不调用doNotify方法,doWait方法会一直自旋,CPU会消耗太大)。被唤醒的线程会自旋直到自旋锁(while循环)里的条件变为false。以下MyWaitNotify2的修改版本展示了这点:

public class MyWaitNotify3{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

三、线程与锁——第三天

java.util.concurrent包不仅提供了第二天介绍的比内置锁更好的锁,还提供了一些通用高效、bug少的并发数据结构和工具。在实际使用中,较之自己实现解决方案,我们应更多地使用这些现成的工具。

1、知识点

//线程池的大小设置为可用处理器数的2倍
int threadPoolSize = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
while(true) {
  Socket socket = server.accept();
  executor.execute(new ConnectionHandler(socket));
}

2、自习

上一篇 下一篇

猜你喜欢

热点阅读