zoukankan      html  css  js  c++  java
  • Java并发编程笔记——技术点汇总

    目录

    · 线程安全

    · 线程安全的实现方法

        · 互斥同步

        · 非阻塞同步

        · 无同步

    · volatile关键字

    · 线程间通信

        · Object.wait()方法

        · Object.notify()方法

        · 编写线程间通信代码的套路

        · 面试题:子线程、主线程交替循环

        · 生产者-消费者问题

        · 哲学家进餐问题

        · 读者-写者问题

    · 线程内共享

    · 定时器

    · JDK5新功能

        · 线程池

        · Callable和Future

        · ReentrantLock

        · ReadWriteLock

        · Condition

        · Semaphore

        · CyclicBarrier

        · CountDownLatch

        · Exchanger

        · BlockingQueue

        · 并发集合

    · 系统峰值评估

        · 峰值参数

        · 评估方法


    线程安全

    1. 线程安全:当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)始终能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。

    2. 线程不安全的示例。期望结果是100000,实际却不是,并且每次执行结果可能不同。

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class Test {
     5 
     6     public static void main(String[] args) throws Exception {
     7         MyNumber myNumber = new MyNumber();
     8         List<MyThread> myThreads = new ArrayList<>();
     9         for (int index = 0; index < 100; index++) {
    10             MyThread myThread = new MyThread(myNumber);
    11             myThreads.add(myThread);
    12             myThread.start();
    13         }
    14         for (MyThread myThread : myThreads) {
    15             myThread.join();
    16         }
    17         System.out.println(myNumber.value());
    18     }
    19 
    20 }
    21 
    22 class MyNumber {
    23     
    24     private int n = 0;
    25     
    26     public int value() {
    27         return n;
    28     }
    29     
    30     public void increate() {
    31         n = n + 1;
    32     }
    33     
    34 }
    35 
    36 class MyThread extends Thread {
    37     
    38     private MyNumber myNumber;
    39     
    40     public MyThread(MyNumber myNumber) {
    41         this.myNumber = myNumber;
    42     }
    43     
    44     @Override
    45     public void run() {
    46         try {
    47             sleep(3000);
    48         } catch (InterruptedException e) {
    49             e.printStackTrace();
    50         }
    51         
    52         for (int index = 0; index < 1000; index++) {
    53             myNumber.increate();
    54         }
    55     }
    56     
    57 }

    线程安全的实现方法

    互斥同步

    1. 最基本互斥同步手段是synchronized关键字。

    2. 例如,上一个线程不安全示例的解决方法如下,两种方法等价,都是对当前对象加锁。

    1 public synchronized void increate() {
    2     n = n + 1;
    3 }
    1 public void increate() {
    2     synchronized (this) {
    3         n = n + 1;
    4     }
    5 }

    3. synchronized关键字是一个重量级操作,因为Java线程是映射到操作系统原生线程上的,如果要阻塞或唤醒线程,都需要操作系统完成,那么线程需要从用户态转换到核心态,状态转换会消耗很多的CPU时间。

    4. 额外注意,“static synchronized”方法与“synchronized (MyClass.class) {…}”等价,都是对类的字节对象加锁。

    非阻塞同步

    1. 互斥同步属于悲观并发策略,非阻塞同步属于乐观并发策略。

    2. 即先行操作,如果没有其他线程争用共享数据,那么算操作成功;如果共享数据有争用,产生冲突,那么再采取其他补偿措施(最常见的就是不断重试,直到成功为止)。由于无须挂起线程,所以是非阻塞的。

    3. java.util.concurrent.atomic.*包下的原子类就是这个原理,核心操作是CAS(Compare and set,利用x86 CPU的CMPXCHG原子指令实现),具体由sun.misc.Unsafe实现。

    4. java.util.concurrent.atomic.*包(JDK5以后)下的原子类分4组(常用已高亮):

        a) AtomicBoolean、AtomicInteger、AtomicLong,线程安全的基本类型的原子性操作。

        b) AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray,线程安全的数组类型的原子性操作,操作的不是整个数组,而是数组中的单个元素。

        c) AtomicLongFieldUpdater、AtomicIntegerFieldUpdater、AtomicReferenceFieldUpdater,基于反射原理对象中的基本类型(长整型、整型和引用类型)进行线程安全的操作。

        d) AtomicReference、AtomicMarkableReference、AtomicStampedReference,线程安全的引用类型及防止ABA问题的引用类型的原子操作。

    5. 线程不安全示例的不阻塞解决方法如下。

     1 class MyNumber {
     2     
     3     private AtomicInteger n = new AtomicInteger(0);
     4     
     5     public int value() {
     6         return n.get();
     7     }
     8     
     9     public void increate() {
    10         n.addAndGet(1);
    11     }
    12     
    13 }

    5. 参考资料:

        a) http://www.cnblogs.com/nullzx/p/4967931.html

        b) http://zl198751.iteye.com/blog/1848575

        c) http://www.cnblogs.com/Mainz/p/3546347.html

    无同步

    如果本来就不涉及共享数据,那么就无须任何同步措施,代码天生就是线程安全的。

    volatile关键字

    1. volatile关键字的主要作用是使变量在多个线程间可见。

    2. 每个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量值的拷贝。当线程执行时,它在自己的工作内存区中操作这些变量。为了存取共享变量,线程通常先获取锁定并去清除它的工作内存区,把这些共享变量从所有线程的工作内存区中正确的装入其中,当线程解锁时保证该工作内存区中的值写回到共享内存中。volatile的作用就是强制线程到主内存(共享内存)去读取变量,而不去线程工作内存区读取。

     1 public class Test {
     2 
     3     public static void main(String[] args) {
     4         MyThread myThread = new MyThread();
     5         myThread.start();
     6         try {
     7             Thread.sleep(3000);
     8         } catch (InterruptedException e) {
     9             e.printStackTrace();
    10         }
    11         myThread.run = false;
    12         System.out.println(myThread.run);
    13     }
    14 
    15 }
    16 
    17 class MyThread extends Thread {
    18     
    19     public volatile boolean run = true;
    20     
    21     @Override
    22     public void run() {
    23         while (run) {
    24             // running
    25         }
    26         System.out.println(Thread.currentThread().getName() + " stoped.");
    27     }
    28     
    29 }

    3. volatile虽然保证了多个线程间的可见性,但不具备同步性。只能算轻量级的synchronized,性能要比synchronized强很多,不会造成阻塞。上一个线程不安全示例用如下方式不能保证结果正确。

     1 class MyNumber {
     2     
     3     private volatile int n = 0;// 错误代码
     4     
     5     public int value() {
     6         return n;
     7     }
     8     
     9     public void increate() {
    10         n = n + 1;
    11     }
    12     
    13 }

    线程间通信

    Object.wait()方法

    1. 使执行instance.wait()方法的当前线程暂停,直至调用该对象instance.notify()或instance.notifyAll()方法唤醒该线程。

    2. 当前线程必须先对该对象instance加锁(即synchronized)才可调用instance.wait()方法,否则抛出异常IllegalMonitorStateException。

    3. 可能存在中断和虚唤醒,所以一般在循环中执行instance.wait()方法。

    4. 综上三点所述,套路如下。

    1 synchronized (instance) {
    2     while (判断是否可占用资源) {
    3         instance.wait();
    4     }
    5 }

    5. 注意Object.wait()方法与Thread.sleep()方法的区别:wait()方法暂停时会释放synchronized的资源,而sleep()方法不会。

    Object.notify()方法

    1. 唤醒执行了instance.wait()方法的线程。如果存在多个这样的线程,则任意唤醒一个。

    2. 当前线程必须先对该对象instance加锁(即synchronized)才可调用instance.notify()方法,否则抛出异常IllegalMonitorStateException。

    3. 综上两点所述,套路如下。

    1 synchronized (instance) {
    2     instance.notify();
    3 }

    4. Object.notifyAll()方法与Object.notify()方法类似,区别是如果存在多个等待的线程,则全部唤醒。

    编写线程间通信代码的套路

    由于执行instance.wait()和instance.notify()方法都必须先synchronized (instance),所以这两个方法应写在一个类中。

    1 public class ExclusiveResource {    // 互斥资源
    2     public synchronized void holdAndUse() {
    3         while (<check resource>) {    // 检查是否可占用资源,否则等待
    4             wait();
    5         }
    6         // 执行占用资源后的代码
    7         notify();
    8     }
    9 }

    面试题:子线程、主线程交替循环

    子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程有循环100次,如此循环50次。

     1 public class Test {
     2 
     3     public static void main(String[] args) {
     4         System.out.println("--------");
     5         
     6         Looper looper = new Looper();
     7         SubThread subThread = new SubThread(looper);
     8         subThread.start();
     9         looper.main();
    10     }
    11 
    12 }
    13 
    14 class SubThread extends Thread {
    15     
    16     private Looper looper;
    17     
    18     public SubThread(Looper looper) {
    19         this.looper = looper;
    20     }
    21     
    22     @Override
    23     public void run() {
    24         looper.sub();
    25     }
    26     
    27 }
    28 
    29 class Looper {
    30     
    31     private boolean runSub = true;
    32     
    33     public void sub() {
    34         for (int i = 0; i < 50; i++) {
    35             synchronized (this) {
    36                 while (!runSub) {
    37                     try {
    38                         wait();
    39                     } catch (InterruptedException e) {
    40                         e.printStackTrace();
    41                     }
    42                 }
    43                 for (int j = 0; j < 10; j++) {
    44                     System.out.println("子线程第" + i + "次循环" + j + "。");
    45                 }
    46                 runSub = false;
    47                 notify();
    48             }
    49         }
    50     }
    51     
    52     public void main() {
    53         for (int i = 0; i < 50; i++) {
    54             synchronized (this) {
    55                 while (runSub) {
    56                     try {
    57                         wait();
    58                     } catch (InterruptedException e) {
    59                         e.printStackTrace();
    60                     }
    61                 }
    62                 for (int j = 0; j < 100; j++) {
    63                     System.out.println("主线程第" + i + "次循环" + j + "。");
    64                 }
    65                 runSub = true;
    66                 notify();
    67             }
    68         }
    69     }
    70     
    71 }

    生产者-消费者问题

    一组生产者进程和一组消费者进程共享一个初始为空、大小为 n 的缓冲区,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息。

      1 import java.util.Random;
      2 import java.util.concurrent.atomic.AtomicInteger;
      3 
      4 public class Test {
      5 
      6     public static void main(String[] args) {
      7         Container container = new Container();
      8         new Producer(container).start();
      9         new Producer(container).start();
     10         new Producer(container).start();
     11         new Consumer(container).start();
     12         new Consumer(container).start();
     13     }
     14 
     15 }
     16 
     17 class Product {
     18     
     19     private static final AtomicInteger COUNT = new AtomicInteger(0);
     20     
     21     private int id;
     22     
     23     public Product() {
     24         id = COUNT.getAndIncrement();
     25     }
     26     
     27     @Override
     28     public String toString() {
     29         return "Product-" + id;
     30     }
     31     
     32 }
     33 
     34 class Container {
     35     
     36     private final static int CAPACITY = 10;
     37     
     38     private Product[] products = new Product[CAPACITY];
     39     
     40     private int size;
     41     
     42     public synchronized void push(Product product) {
     43         while (size >= CAPACITY) {
     44             try {
     45                 wait();
     46             } catch (InterruptedException e) {
     47                 e.printStackTrace();
     48             }
     49         }
     50         products[size] = product;
     51         size++;
     52         notify();
     53     }
     54     
     55     public synchronized Product pop() {
     56         while (size <= 0) {
     57             try {
     58                 wait();
     59             } catch (InterruptedException e) {
     60                 e.printStackTrace();
     61             }
     62         }
     63         size--;
     64         Product product = products[size];
     65         notify();
     66         return product;
     67     }
     68     
     69 }
     70 
     71 class Producer extends Thread {
     72     
     73     private static int count;
     74     
     75     private Container container;
     76     
     77     public Producer(Container container) {
     78         super("Producer-" + count++);
     79         this.container = container;
     80     }
     81     
     82     @Override
     83     public void run() {
     84         while (true) {
     85             Product product = new Product();
     86             System.out.println(getName() + " produced " + product);
     87             container.push(product);
     88             
     89             try {
     90                 Thread.sleep(new Random().nextInt(500));
     91             } catch (InterruptedException e) {
     92                 e.printStackTrace();
     93             }
     94         }
     95     }
     96     
     97 }
     98 
     99 class Consumer extends Thread {
    100     
    101     private static int count;
    102     
    103     private Container container;
    104     
    105     public Consumer(Container container) {
    106         super("Consumer-" + count++);
    107         this.container = container;
    108     }
    109     
    110     @Override
    111     public void run() {
    112         while (true) {
    113             Product product = container.pop();
    114             System.out.println(getName() + " consumed " + product);
    115             
    116             try {
    117                 Thread.sleep(new Random().nextInt(500));
    118             } catch (InterruptedException e) {
    119                 e.printStackTrace();
    120             }
    121         }
    122     }
    123     
    124 }

    哲学家进餐问题

    一张圆桌上坐着 5 名哲学家,桌子上每两个哲学家之间摆了一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、右两根筷子(一根一根拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

      1 import java.util.Random;
      2 
      3 public class Test {
      4 
      5     public static void main(String[] args) {
      6         Table table = new Table();
      7         new Philosopher(table).start();
      8         new Philosopher(table).start();
      9         new Philosopher(table).start();
     10         new Philosopher(table).start();
     11         new Philosopher(table).start();
     12     }
     13 
     14 }
     15 
     16 class Chopstick {
     17     
     18     private static int count;
     19     
     20     private int id;
     21     
     22     private boolean isTakenUp;
     23     
     24     public Chopstick() {
     25         id = count++;
     26     }
     27     
     28     public boolean isTakenUp() {
     29         return isTakenUp;
     30     }
     31 
     32     public void setTakenUp(boolean isTakenUp) {
     33         this.isTakenUp = isTakenUp;
     34     }
     35 
     36     @Override
     37     public String toString() {
     38         return "Chopstick-" + id;
     39     }
     40     
     41 }
     42 
     43 class Table {
     44     
     45     private static final int CAPACITY = 5;
     46     
     47     private Chopstick[] chopsticks = new Chopstick[CAPACITY];
     48     
     49     public Table() {
     50         for (int index = 0; index < chopsticks.length; index++) {
     51             chopsticks[index] = new Chopstick();
     52         }
     53     }
     54     
     55     public synchronized void takeUp(int leftChopstickIndex, int rightChopstickIndex) {
     56         Chopstick leftChopstick = chopsticks[leftChopstickIndex];
     57         Chopstick rightChopstick = chopsticks[rightChopstickIndex];
     58         while (leftChopstick.isTakenUp() || rightChopstick.isTakenUp()) {
     59             try {
     60                 wait();
     61             } catch (InterruptedException e) {
     62                 e.printStackTrace();
     63             }
     64         }
     65         leftChopstick.setTakenUp(true);
     66         System.out.println(leftChopstick + " has been taken up.");
     67         rightChopstick.setTakenUp(true);
     68         System.out.println(rightChopstick + " has been taken up.");
     69         notifyAll();
     70     }
     71     
     72     public synchronized void putDown(int leftChopstickIndex, int rightChopstickIndex) {
     73         Chopstick leftChopstick = chopsticks[leftChopstickIndex];
     74         Chopstick rightChopstick = chopsticks[rightChopstickIndex];
     75         leftChopstick.setTakenUp(false);
     76         System.out.println(leftChopstick + " has been put down.");
     77         rightChopstick.setTakenUp(false);
     78         System.out.println(rightChopstick + " has been put down.");
     79         notifyAll();
     80     }
     81     
     82 }
     83 
     84 class Philosopher extends Thread {
     85     
     86     private static final int MAX_COUNT = 5;
     87     
     88     private static int count;
     89     
     90     private int id;
     91     
     92     private Table table;
     93     
     94     public Philosopher(Table table) {
     95         if (count >= MAX_COUNT) {
     96             throw new RuntimeException("Cannot create Philosopher instance more than " + MAX_COUNT + ".");
     97         }
     98         id = count++;
     99         setName("Philosopher-" + id);
    100         this.table = table;
    101     }
    102 
    103     @Override
    104     public void run() {
    105         final int leftChopstickIndex = id;
    106         final int rightChopstickIndex = (id + 1) % MAX_COUNT;
    107         while (true) {
    108             table.takeUp(leftChopstickIndex, rightChopstickIndex);
    109             System.out.println(getName() + " is eating.");
    110             
    111             try {
    112                 Thread.sleep(new Random().nextInt(500));
    113             } catch (InterruptedException e) {
    114                 e.printStackTrace();
    115             }
    116             
    117             System.out.println(getName() + " has eaten.");
    118             table.putDown(leftChopstickIndex, rightChopstickIndex);
    119             
    120             try {
    121                 Thread.sleep(new Random().nextInt(500));
    122             } catch (InterruptedException e) {
    123                 e.printStackTrace();
    124             }
    125         }
    126     }
    127     
    128 }

    读者-写者问题

    有读者和写者两组并发线程,共享一个文件,当两个或以上的读线程同时访问共享数据时不会产生副作用,但若某个写线程和其他线程(读线程或写线程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:

    1. 允许多个读者可以同时对文件执行读操作;

    2. 只允许一个写者往文件中写信息;

    3. 任一写者在完成写操作之前不允许其他读者或写者工作;

    4. 写者执行写操作前,应让已有的读者和写者全部退出。

      1 import java.util.HashMap;
      2 import java.util.Map;
      3 import java.util.Random;
      4 
      5 public class Test {
      6 
      7     public static void main(String[] args) {
      8         File file = new File();
      9         new Reader(file).start();
     10         new Reader(file).start();
     11         new Reader(file).start();
     12         new Writer(file).start();
     13         new Writer(file).start();
     14     }
     15 
     16 }
     17 
     18 class File {
     19     
     20     public enum Status {
     21         idle, reading, writing,
     22     }
     23     
     24     private Map<Long, Status> threadStatuses = new HashMap<>();
     25     
     26     public synchronized void beginRead() {
     27         outer: while (true) {
     28             for (Status status : threadStatuses.values()) {
     29                 if (Status.writing.equals(status)) {
     30                     try {
     31                         wait();
     32                     } catch (InterruptedException e) {
     33                         e.printStackTrace();
     34                     }
     35                     continue outer;
     36                 }
     37             }
     38             break;
     39         }
     40         threadStatuses.put(Thread.currentThread().getId(), Status.reading);
     41         notifyAll();
     42     }
     43     
     44     public synchronized void endRead() {
     45         threadStatuses.put(Thread.currentThread().getId(), Status.idle);
     46         notifyAll();
     47     }
     48     
     49     public synchronized void beginWrite() {
     50         outer: while (true) {
     51             for (Status status : threadStatuses.values()) {
     52                 if (!Status.idle.equals(status)) {
     53                     try {
     54                         wait();
     55                     } catch (InterruptedException e) {
     56                         e.printStackTrace();
     57                     }
     58                     continue outer;
     59                 }
     60             }
     61             break;
     62         }
     63         threadStatuses.put(Thread.currentThread().getId(), Status.writing);
     64         notifyAll();
     65     }
     66     
     67     public synchronized void endWrite() {
     68         threadStatuses.put(Thread.currentThread().getId(), Status.idle);
     69         notifyAll();
     70     }
     71     
     72 }
     73 
     74 class Reader extends Thread {
     75     
     76     private static int count;
     77     
     78     private File file;
     79     
     80     public Reader(File file) {
     81         super("Reader-" + count++);
     82         this.file = file;
     83     }
     84     
     85     @Override
     86     public void run() {
     87         while (true) {
     88             file.beginRead();
     89             System.out.println(Thread.currentThread().getName() + " is reading.");
     90             
     91             try {
     92                 Thread.sleep(new Random().nextInt(500));
     93             } catch (InterruptedException e) {
     94                 e.printStackTrace();
     95             }
     96             
     97             System.out.println(Thread.currentThread().getName() + " has read.");
     98             file.endRead();
     99             
    100             try {
    101                 Thread.sleep(new Random().nextInt(500));
    102             } catch (InterruptedException e) {
    103                 e.printStackTrace();
    104             }
    105         }
    106     }
    107     
    108 }
    109 
    110 class Writer extends Thread {
    111     
    112     private static int count;
    113     
    114     private File file;
    115     
    116     public Writer(File file) {
    117         super("Writer-" + count++);
    118         this.file = file;
    119     }
    120     
    121     @Override
    122     public void run() {
    123         while (true) {
    124             file.beginWrite();
    125             System.out.println(Thread.currentThread().getName() + " is writing.");
    126             
    127             try {
    128                 Thread.sleep(new Random().nextInt(500));
    129             } catch (InterruptedException e) {
    130                 e.printStackTrace();
    131             }
    132             
    133             System.out.println(Thread.currentThread().getName() + " has wroten.");
    134             file.endWrite();
    135             
    136             try {
    137                 Thread.sleep(new Random().nextInt(500));
    138             } catch (InterruptedException e) {
    139                 e.printStackTrace();
    140             }
    141         }
    142     }
    143     
    144 }

    线程内共享

    1. ThreadLocal存放的值是线程内共享的,线程间互斥的。主要用于线程内共享数据,避免通过参数来传递,这样能优雅地解决一些实际问题。

    2. ThreadLocal的原理是为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象。

    3. 数据库连接工具类的举例。

     1 import java.sql.Connection;
     2 import java.sql.DriverManager;
     3 import java.sql.SQLException;
     4 
     5 public class ConnectionUtils {
     6     
     7     private static ThreadLocal<Connection> threadLocalConnection = new ThreadLocal<Connection>();
     8     
     9     public static Connection getCurrent() throws SQLException {
    10         Connection connection = threadLocalConnection.get();
    11         if (connection == null || connection.isClosed()) {
    12             connection = DriverManager.getConnection("...");
    13             threadLocalConnection.set(connection);
    14         }
    15         return connection;
    16     }
    17     
    18     public static void close() throws SQLException {
    19         Connection connection = threadLocalConnection.get();
    20         if (connection != null && !connection.isClosed()) {
    21             connection.close();
    22             threadLocalConnection.remove();
    23         }
    24     }
    25 
    26 }

    定时器

    1. 定时器使用JDK的Timer和TimerTask;

    2. 复杂的定时器可考虑使用Quartz框架。

     1 import java.util.Timer;
     2 import java.util.TimerTask;
     3 
     4 public class Test {
     5 
     6     public static void main(String[] args) {
     7         new Timer().schedule(new MyTimerTask(), 3000);;
     8     }
     9 
    10 }
    11 
    12 class MyTimerTask extends TimerTask {
    13 
    14     @Override
    15     public void run() {
    16         System.out.println("Hello World.");
    17     }
    18     
    19 }

    JDK5新功能

    线程池

    1. 线程池优点:

        a) 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

        b) 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。

        c) 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    2. 四种线程池:

        a) 固定大小线程池:Executors.newFixedThreadPool(int nThreads)。

        b) 缓存线程池:Executors.newCachedThreadPool(),线程池大小随提交任务数增加而增加。

        c) 单一线程池:Executors.newSingleThreadExecutor()。

        d) 调度线程池:Executors.newScheduledThreadPool(int corePoolSize),调度执行线程。

    3. 关闭线程池:

        a) 任务执行完毕后关闭:ExecutorService.shutdown()。

        b) 不论任务是否完成立即关闭:ExecutorService.shutdownNow()。

    4. 示例。

     1 import java.util.Random;
     2 import java.util.concurrent.ExecutorService;
     3 import java.util.concurrent.Executors;
     4 import java.util.concurrent.atomic.AtomicInteger;
     5 
     6 public class Test {
     7 
     8     public static void main(String[] args) {
     9         ExecutorService threadPool = Executors.newFixedThreadPool(3);
    10 //        ExecutorService threadPool = Executors.newCachedThreadPool();
    11 //        ExecutorService threadPool = Executors.newSingleThreadExecutor();
    12         Task0 task0 = new Task0();
    13         Task1 task1 = new Task1();
    14         int taskCount = 20;
    15         for (int index = 0; index < taskCount; index++) {
    16             if (index % 2 == 0) {
    17                 threadPool.execute(task0);
    18             } else {
    19                 threadPool.execute(task1);
    20             }
    21         }
    22         System.out.println(taskCount + " tasks have been committed.");
    23         threadPool.shutdown();
    24     }
    25 
    26 }
    27 
    28 class Task0 implements Runnable {
    29     
    30     private AtomicInteger count = new AtomicInteger(0);
    31 
    32     @Override
    33     public void run() {
    34         System.out.println(Thread.currentThread().getName() + " is executing Task0-" + count.getAndIncrement());
    35         try {
    36             Thread.sleep(new Random().nextInt(500));
    37         } catch (InterruptedException e) {
    38             e.printStackTrace();
    39         }
    40 
    41     }
    42     
    43 }
    44 
    45 class Task1 implements Runnable {
    46     
    47     private AtomicInteger count = new AtomicInteger(0);
    48 
    49     @Override
    50     public void run() {
    51         System.out.println(Thread.currentThread().getName() + " is executing Task1-" + count.getAndIncrement());
    52         try {
    53             Thread.sleep(new Random().nextInt(500));
    54         } catch (InterruptedException e) {
    55             e.printStackTrace();
    56         }
    57     }
    58     
    59 }

    Callable和Future

    1. Callable接口:异步任务。

    2. Future接口:异步任务的结果。

     1 import java.util.Random;
     2 import java.util.concurrent.Callable;
     3 import java.util.concurrent.ExecutionException;
     4 import java.util.concurrent.ExecutorService;
     5 import java.util.concurrent.Executors;
     6 import java.util.concurrent.Future;
     7 
     8 public class Test {
     9 
    10     public static void main(String[] args) throws InterruptedException, ExecutionException {
    11         ExecutorService threadPool = Executors.newFixedThreadPool(3);
    12         Future<Integer> future0 = threadPool.submit(new Task());
    13         Future<Integer> future1 = threadPool.submit(new Task());
    14         System.out.println("Two tasks have been committed.");
    15         System.out.println("future0=" + future0.get());
    16         System.out.println("future1=" + future1.get());
    17         threadPool.shutdown();
    18     }
    19 
    20 }
    21 
    22 class Task implements Callable<Integer> {
    23 
    24     @Override
    25     public Integer call() throws Exception {
    26         try {
    27             Thread.sleep(new Random().nextInt(500));
    28         } catch (InterruptedException e) {
    29             e.printStackTrace();
    30         }
    31         return new Random().nextInt();
    32     }
    33 
    34 }

    3. CompletionService接口:提交一组Callable任务,哪个先执行完则先返回结果。

     1 import java.util.Random;
     2 import java.util.concurrent.Callable;
     3 import java.util.concurrent.CompletionService;
     4 import java.util.concurrent.ExecutionException;
     5 import java.util.concurrent.ExecutorCompletionService;
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 import java.util.concurrent.Future;
     9 import java.util.concurrent.atomic.AtomicInteger;
    10 
    11 public class Test {
    12 
    13     public static void main(String[] args) throws InterruptedException, ExecutionException {
    14         ExecutorService threadPool = Executors.newFixedThreadPool(3);
    15         CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool);
    16         Task task = new Task();
    17         int taskCount = 10;
    18         for (int index = 0; index < taskCount; index++) {
    19             completionService.submit(task);
    20         }
    21         for (int index = 0; index < taskCount; index++) {
    22             Future<Integer> future = completionService.take();
    23             System.out.println("future" + index + "=" + future.get());
    24         }
    25         threadPool.shutdown();
    26     }
    27 
    28 }
    29 
    30 class Task implements Callable<Integer> {
    31     
    32     private AtomicInteger count = new AtomicInteger(0);
    33 
    34     @Override
    35     public Integer call() throws Exception {
    36         int ret = count.getAndIncrement();
    37         try {
    38             Thread.sleep(new Random().nextInt(500));
    39         } catch (InterruptedException e) {
    40             e.printStackTrace();
    41         }
    42         return ret;
    43     }
    44 
    45 }

    ReentrantLock

    1. java.util.concurrent.locks.ReentrantLock与synchronized作用类似,只是更面向对象。

    2. 注意:Lock应该与try { … } finally { … }使用,保证锁一定释放。

    3. 线程不安全示例的Lock写法。

     1 class MyNumber {
     2     
     3     private Lock lock = new ReentrantLock();
     4     
     5     private int n = 0;
     6     
     7     public int value() {
     8         return n;
     9     }
    10     
    11     public void increate() {
    12         try {
    13             lock.lock();
    14             n = n + 1;
    15         } finally {
    16             lock.unlock();
    17         }
    18     }
    19     
    20 }

    ReadWriteLock

    1. java.util.concurrent.locks.ReadWriteLock包含读锁和写锁,类似“读者-写者问题”中的读者和写者,即多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。

    2. “读者-写者问题”的读锁、写锁写法。

      1 import java.util.Random;
      2 import java.util.concurrent.locks.ReadWriteLock;
      3 import java.util.concurrent.locks.ReentrantReadWriteLock;
      4 
      5 public class Test {
      6 
      7     public static void main(String[] args) {
      8         File file = new File();
      9         new Reader(file).start();
     10         new Reader(file).start();
     11         new Reader(file).start();
     12         new Writer(file).start();
     13         new Writer(file).start();
     14     }
     15 
     16 }
     17 
     18 class File {
     19     
     20     private ReadWriteLock lock = new ReentrantReadWriteLock();
     21     
     22     public void beginRead() {
     23         lock.readLock().lock();
     24     }
     25     
     26     public void endRead() {
     27         lock.readLock().unlock();
     28     }
     29     
     30     public void beginWrite() {
     31         lock.writeLock().lock();
     32     }
     33     
     34     public void endWrite() {
     35         lock.writeLock().unlock();
     36     }
     37     
     38 }
     39 
     40 class Reader extends Thread {
     41     
     42     private static int count;
     43     
     44     private File file;
     45     
     46     public Reader(File file) {
     47         super("Reader-" + count++);
     48         this.file = file;
     49     }
     50     
     51     @Override
     52     public void run() {
     53         while (true) {
     54             try {
     55                 file.beginRead();
     56                 System.out.println(Thread.currentThread().getName() + " is reading.");
     57                 
     58                 try {
     59                     Thread.sleep(new Random().nextInt(500));
     60                 } catch (InterruptedException e) {
     61                     e.printStackTrace();
     62                 }
     63                 
     64                 System.out.println(Thread.currentThread().getName() + " has read.");
     65             } finally {
     66                 file.endRead();
     67             }
     68             
     69             try {
     70                 Thread.sleep(new Random().nextInt(500));
     71             } catch (InterruptedException e) {
     72                 e.printStackTrace();
     73             }
     74         }
     75     }
     76     
     77 }
     78 
     79 class Writer extends Thread {
     80     
     81     private static int count;
     82     
     83     private File file;
     84     
     85     public Writer(File file) {
     86         super("Writer-" + count++);
     87         this.file = file;
     88     }
     89     
     90     @Override
     91     public void run() {
     92         while (true) {
     93             try {
     94                 file.beginWrite();
     95                 System.out.println(Thread.currentThread().getName() + " is writing.");
     96                 
     97                 try {
     98                     Thread.sleep(new Random().nextInt(500));
     99                 } catch (InterruptedException e) {
    100                     e.printStackTrace();
    101                 }
    102                 
    103                 System.out.println(Thread.currentThread().getName() + " has wroten.");
    104             } finally {
    105                 file.endWrite();
    106             }
    107             
    108             try {
    109                 Thread.sleep(new Random().nextInt(500));
    110             } catch (InterruptedException e) {
    111                 e.printStackTrace();
    112             }
    113         }
    114     }
    115     
    116 }

    3. 使用ReadWriteLock优化缓存:第1个线程写,后续线程只读(类似ReentrantReadWriteLock JDK文档中的示例)。

     1 import java.util.HashMap;
     2 import java.util.Map;
     3 import java.util.Random;
     4 import java.util.concurrent.locks.ReadWriteLock;
     5 import java.util.concurrent.locks.ReentrantReadWriteLock;
     6 
     7 class Cache {
     8     
     9     private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    10     
    11     private Map<Object, Object> data = new HashMap<>();
    12     
    13     public Object getValue(Object key) {
    14         readWriteLock.readLock().lock();
    15         Object value = null;
    16         try {
    17             value = data.get(key);
    18             if (value == null) {
    19                 readWriteLock.readLock().unlock();
    20                 readWriteLock.writeLock().lock();
    21                 try {
    22                     if (value == null) {
    23                         value = new Random().nextInt(1000);    // 模拟去数据库查询数据
    24                     }
    25                 } finally {
    26                     readWriteLock.writeLock().unlock();
    27                 }
    28                 readWriteLock.readLock().lock();
    29             }
    30             
    31         } finally {
    32             readWriteLock.readLock().unlock();
    33         }
    34         return value;
    35     }
    36     
    37 }

    Condition

    1. java.util.concurrent.locks.Condition类似Object.wait()和Object.notify()/Object.notifyAll()方法,实现了线程间通信。

    2. “子线程、主线程交替循环”面试题的Condition写法。

     1 class Looper {
     2     
     3     private Lock lock = new ReentrantLock();
     4     
     5     private Condition condition = lock.newCondition();
     6     
     7     private boolean runSub = true;
     8     
     9     public void sub() {
    10         for (int i = 0; i < 50; i++) {
    11             lock.lock();
    12             try {
    13                 while (!runSub) {
    14                     try {
    15                         condition.await();
    16                     } catch (InterruptedException e) {
    17                         e.printStackTrace();
    18                     }
    19                 }
    20                 for (int j = 0; j < 10; j++) {
    21                     System.out.println("子线程第" + i + "次循环" + j + "。");
    22                 }
    23                 runSub = false;
    24                 condition.signal();
    25             } finally {
    26                 lock.unlock();
    27             }
    28         }
    29     }
    30     
    31     public void main() {
    32         for (int i = 0; i < 50; i++) {
    33             lock.lock();
    34             try {
    35                 while (runSub) {
    36                     try {
    37                         condition.await();
    38                     } catch (InterruptedException e) {
    39                         e.printStackTrace();
    40                     }
    41                 }
    42                 for (int j = 0; j < 100; j++) {
    43                     System.out.println("主线程第" + i + "次循环" + j + "。");
    44                 }
    45                 runSub = true;
    46                 condition.signal();
    47             } finally {
    48                 lock.unlock();
    49             }
    50         }
    51     }
    52     
    53 }

    3. 与Object.wait()和Object.notify不同的是,Condition能实现多路暂停和唤醒,具体示例参考Condition JDK文档。

    Semaphore

    1. java.util.concurrent.Semaphore信号量可控制同时访问资源的线程个数。

    2. Semaphore可以由一个线程获得锁,而由另一个线程释放锁,这可以应用与死锁恢复的场景。

    3. Semaphore适合做网站流量控制,拿到信号量的线程可以进入,否则只能等待。

    4. 示例。

     1 import java.util.concurrent.ExecutorService;
     2 import java.util.concurrent.Executors;
     3 import java.util.concurrent.Semaphore;
     4 
     5 public class Test {
     6 
     7     public static void main(String[] args) {
     8         ExecutorService threadPool = Executors.newCachedThreadPool();
     9         Semaphore semaphore = new Semaphore(3);
    10         Task task = new Task(semaphore);
    11         for (int index = 0; index < 10; index++) {
    12             threadPool.execute(task);
    13         }
    14         threadPool.shutdown();
    15     }
    16 
    17 }
    18 
    19 class Task implements Runnable {
    20     
    21     private Semaphore semaphore;
    22     
    23     public Task(Semaphore semaphore) {
    24         this.semaphore = semaphore;
    25     }
    26 
    27     @Override
    28     public void run() {
    29         try {
    30             semaphore.acquire();
    31             System.out.println(semaphore.availablePermits() + " permit(s) left.");
    32             Thread.sleep(1000);
    33             System.out.println(Thread.currentThread().getId() + " is done.");
    34             
    35         } catch (InterruptedException e) {
    36             e.printStackTrace();
    37         } finally {
    38             semaphore.release();
    39         }
    40     }
    41     
    42 }

    CyclicBarrier

    1. java.util.concurrent.CyclicBarrier阻塞一组线程,直到达到指定的阻塞线程数量后才停止阻塞。类似生活中集合的场景,10小伙伴约定地点集合,先到着等待,等全都到齐才出发。

    2. 示例。

     1 import java.util.Random;
     2 import java.util.concurrent.BrokenBarrierException;
     3 import java.util.concurrent.CyclicBarrier;
     4 import java.util.concurrent.ExecutorService;
     5 import java.util.concurrent.Executors;
     6 
     7 public class Test {
     8 
     9     public static void main(String[] args) {
    10         ExecutorService threadPool = Executors.newCachedThreadPool();
    11         CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    12         Task task = new Task(cyclicBarrier);
    13         for (int index = 0; index < 9; index++) {
    14             threadPool.execute(task);
    15         }
    16         threadPool.shutdown();
    17     }
    18 
    19 }
    20 
    21 class Task implements Runnable {
    22     
    23     private CyclicBarrier cyclicBarrier;
    24     
    25     public Task(CyclicBarrier cyclicBarrier) {
    26         this.cyclicBarrier = cyclicBarrier;
    27     }
    28 
    29     @Override
    30     public void run() {
    31         try {
    32             Thread.sleep(new Random().nextInt(500));
    33         } catch (InterruptedException e) {
    34             e.printStackTrace();
    35         }
    36         System.out.println(Thread.currentThread().getName() + "执行完毕。当前等待的线程个数是" + cyclicBarrier.getNumberWaiting() + "。");
    37         try {
    38             cyclicBarrier.await();
    39         } catch (InterruptedException e) {
    40             e.printStackTrace();
    41         } catch (BrokenBarrierException e) {
    42             e.printStackTrace();
    43         }
    44     }
    45     
    46 }

    CountDownLatch

    1. java.util.concurrent.CountDownLatch倒数器,执行CountDownLatch.countDown()计数器减1,当计数器为0时,则所有等待的线程停止阻塞。

    2. 可实现一个线程等待多个线程,也可实现多个线程等待一个线程。以3个运动员、1个裁判发令并统计结果为示例。

     1 import java.util.Random;
     2 import java.util.concurrent.CountDownLatch;
     3 
     4 public class Test {
     5 
     6     public static void main(String[] args) {
     7         CountDownLatch beginCountDownLatch = new CountDownLatch(1);
     8         CountDownLatch endCountDownLatch = new CountDownLatch(3);
     9         new Runner(beginCountDownLatch, endCountDownLatch).start();
    10         new Runner(beginCountDownLatch, endCountDownLatch).start();
    11         new Runner(beginCountDownLatch, endCountDownLatch).start();
    12         new Judge(beginCountDownLatch, endCountDownLatch).start();
    13     }
    14 
    15 }
    16 
    17 class Runner extends Thread {
    18     
    19     private CountDownLatch beginCountDownLatch;
    20     
    21     private CountDownLatch endCountDownLatch;
    22     
    23     public Runner(CountDownLatch beginCountDownLatch, CountDownLatch endCountDownLatch) {
    24         this.beginCountDownLatch = beginCountDownLatch;
    25         this.endCountDownLatch = endCountDownLatch;
    26     }
    27 
    28     @Override
    29     public void run() {
    30         try {
    31             System.out.println("Runner " + getId() + " is ready.");
    32             beginCountDownLatch.await();
    33             Thread.sleep(new Random().nextInt(500));
    34             System.out.println("Runner " + getId() + " has run.");
    35             endCountDownLatch.countDown();
    36         } catch (InterruptedException e) {
    37             e.printStackTrace();
    38         }
    39     }
    40     
    41 }
    42 
    43 class Judge extends Thread {
    44     
    45     private CountDownLatch beginCountDownLatch;
    46     
    47     private CountDownLatch endCountDownLatch;
    48     
    49     public Judge(CountDownLatch beginCountDownLatch, CountDownLatch endCountDownLatch) {
    50         this.beginCountDownLatch = beginCountDownLatch;
    51         this.endCountDownLatch = endCountDownLatch;
    52     }
    53 
    54     @Override
    55     public void run() {
    56         try {
    57             Thread.sleep(3000);
    58             System.out.println("Judge says "Go!"");
    59             beginCountDownLatch.countDown();
    60             System.out.println("Judge is waiting for runners.");
    61             endCountDownLatch.await();
    62             System.out.println("Judge has waited.");
    63             
    64         } catch (InterruptedException e) {
    65             e.printStackTrace();
    66         }
    67     }
    68     
    69 }

    Exchanger

    1. java.util.concurrent.Exchanger可实现两个线程间的数据交互。先执行Exchanger.exchange()方法的线程被阻塞,直到另一个线程也执行Exchanger.exchange()方法才停止,两条线程交换完数据后继续执行。

    2. 示例。

     1 import java.util.Random;
     2 import java.util.UUID;
     3 import java.util.concurrent.Exchanger;
     4 
     5 public class Test {
     6 
     7     public static void main(String[] args) {
     8         Exchanger<String> exchanger = new Exchanger<String>();
     9         new MyThread(exchanger).start();
    10         new MyThread(exchanger).start();
    11     }
    12 
    13 }
    14 
    15 class MyThread extends Thread {
    16     
    17     private Exchanger<String> exchanger;
    18     
    19     public MyThread(Exchanger<String> exchanger) {
    20         this.exchanger = exchanger;
    21     }
    22 
    23     @Override
    24     public void run() {
    25         String value = UUID.randomUUID().toString();
    26         System.out.println(Thread.currentThread().getName() + "准备用" + value + "交换。");
    27         String newValue = null;
    28         try {
    29             Thread.sleep(new Random().nextInt(5000));
    30             newValue = exchanger.exchange(value);
    31         } catch (InterruptedException e) {
    32             e.printStackTrace();
    33         }
    34         System.out.println(Thread.currentThread().getName() + "交换得到" + newValue + "。");
    35     }
    36     
    37 }

    BlockingQueue

    1. java.util.concurrent.BlockingQueue为阻塞队列,类似“生产者-消费者问题”中的容器,“满值欲加”和“无值欲取”都会被阻塞。

    2. 查JDK文档可知:

                                     场景+操作

    结果

    “满值欲加”

    “无值欲取”

    检查

    抛出异常

    add(e)

    remove()

    element()

    返回值

    offer(e)

    poll()

    peek()

    阻塞

    put(e)

    take()

    不可用

    3. 固定长度阻塞队列ArrayBlockingQueue,不固定长度阻塞队列LinkedBlockingDeque。

    4. “生产者-消费者问题”的BlockingQueue写法。

     1 class Container {
     2     
     3     private final static int CAPACITY = 10;
     4     
     5     private BlockingQueue<Product> blocingQueue = new ArrayBlockingQueue<Product>(CAPACITY);
     6     
     7     public void push(Product product) {
     8         try {
     9             blocingQueue.put(product);
    10         } catch (InterruptedException e) {
    11             e.printStackTrace();
    12         }
    13     }
    14     
    15     public Product pop() {
    16         Product product = null;
    17         try {
    18             product = blocingQueue.take();
    19         } catch (InterruptedException e) {
    20             e.printStackTrace();
    21         }
    22         return product;
    23     }
    24     
    25 }

    5. 两个具有1个容量的阻塞队列可实现线程间通信。“子线程、主线程交替循环问题”的BlockingQueue写法。

     1 class Looper {
     2     
     3     private BlockingQueue<Integer> subBlockingQueue = new ArrayBlockingQueue<Integer>(1);
     4     
     5     private BlockingQueue<Integer> mainBlockingQueue = new ArrayBlockingQueue<Integer>(1);
     6     
     7     public Looper() {
     8         subBlockingQueue.add(1);
     9     }
    10     
    11     public void sub() {
    12         for (int i = 0; i < 50; i++) {
    13             try {
    14                 subBlockingQueue.take();
    15             } catch (InterruptedException e) {
    16                 e.printStackTrace();
    17             }
    18             for (int j = 0; j < 10; j++) {
    19                 System.out.println("子线程第" + i + "次循环" + j + "。");
    20             }
    21             mainBlockingQueue.add(1);
    22         }
    23     }
    24     
    25     public void main() {
    26         for (int i = 0; i < 50; i++) {
    27             try {
    28                 mainBlockingQueue.take();
    29             } catch (InterruptedException e) {
    30                 e.printStackTrace();
    31             }
    32             for (int j = 0; j < 100; j++) {
    33                 System.out.println("主线程第" + i + "次循环" + j + "。");
    34             }
    35             subBlockingQueue.add(1);
    36         }
    37     }
    38     
    39 }

    并发集合

    1. 并发集合与传统集合、同步集合对应关系。

    传统集合(非线程安全)

    同步集合(线程安全)

    并发集合(线程安全)

    ArrayList

    Vector

    Collections.synchronizedCollection(...)

    Collections.synchronizedList(...)

    CopyOnWriteArrayList

    Set

    Collections.synchronizedSet(...)

    CopyOnWriteArraySet

    TreeSet

    Collections.synchronizedNavigableSet(...)

    Collections.synchronizedSortedSet(...)

    ConcurrentSkipListSet

    HashMap

    Hashtable

    Collections.synchronizedMap(...)

    ConcurrentHashMap

    TreeMap

    Collections.synchronizedNavigableMap(...)

    Collections.synchronizedSortedMap(...)

    ConcurrentSkipListMap

    2. 并发集合与同步集合的区别。

        a) 并发集合尽量避免synchronized,提供并发性;

        b) 并发集合定义了一些并发安全的复合操作,并且保证并发环境下的迭代操作不会出错。示例如下。

     1 import java.util.ArrayList;
     2 import java.util.Iterator;
     3 import java.util.List;
     4 import java.util.Vector;
     5 import java.util.concurrent.CopyOnWriteArrayList;
     6 
     7 public class Test {
     8 
     9     public static void main(String[] args) {
    10 //        List<String> list = new ArrayList<>();    // 抛出ConcurrentModificationException,或结果不正确
    11 //        List<String> list = new Vector<>();    // 抛出ConcurrentModificationException,或结果不正确
    12         List<String> list = new CopyOnWriteArrayList<String>();
    13         list.add("s1");
    14         list.add("s2");
    15         list.add("s3");
    16         list.add("s4");
    17         list.add("s5");
    18         for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    19             String current = iterator.next();
    20             if ("s1".equals(current)
    21                     || "s3".equals(current)
    22                     || "s5".equals(current)) {
    23                 list.remove(current);
    24             } else {
    25                 System.out.println(current);
    26             }
    27         }
    28     }
    29 
    30 }

    系统峰值评估

    峰值参数

    1. PV(Page View):网站的总访问量、页面浏览量或点击量,用户每刷新一次就会被记录一次。

    2. UV(Unique Visitor):访问网站的一台电脑客户端为一个访客。一般来讲,时间上以00:00至24:00之间相同IP的客户端只记录一次。

    3. QPS(Query Per Second):每秒查询数。QPS很大程度上代表了系统业务上的繁忙程度,每次查询的背后,可能对应着多次磁盘I/O,多次网络请求,多次CPU时间片等。通过QPS可以非常直观的了解当前系统业务情况,一旦当前QPS超过所设定的预警阀值,可以考虑增加机器对集群扩容,以免压力过大导致宕机。可以根据前期的压力测试得到估值,在结合后期综合运维情况,估算出阀值。

    4. RT(Response Time):请求的响应时间。这个指标非常关键,直接说明前端用户的体验,任何系统设计师都想降低RT。

    5. 还有设计CPU、内存、网络、磁盘等情况的参数。

    评估方法

    1. 一般通过开发、运维、测试以及业务等相关人员,综合出系统的一系列阀值,然后根据关键阀值(如QPS、RT等)对系统进行有效变更。

    2. 进行多轮压力测试以后,可以对系统进行峰值评估,采用80/20原则,即80%的PV将在20%的时间内达到。这样计算出系统峰值QPS:

    峰值QPS = ( 总PV * 80% ) / ( 60 * 60 * 24 * 20% )

    总峰值QPS除以单台机器所能承受的最高QPS就是须要的机器数量:

    机器数 = 总峰值QPS / 压测得出的单机极限QPS

    还要考虑大型促销活动和双十一、双十二热点事件、遭受DDoS攻击等情况,系统的开发和维护人急需了解当前系统运行的状态和负载情况。

    作者:netoxi
    出处:http://www.cnblogs.com/netoxi
    本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。

  • 相关阅读:
    codeforces 820 D. Mister B and PR Shifts(思维)
    codeforces 820 C. Mister B and Boring Game(找规律)
    玲珑杯 1137
    codeforces 817 D. Imbalanced Array(单调栈+思维)
    Atcoder D
    Atcoder C
    Atcode B
    codeforces 816 E. Karen and Supermarket(树形dp)
    codeforces 816 D. Karen and Test(逆元+思维+组合数)
    codeforces 816 C. Karen and Game(模拟+思维)
  • 原文地址:https://www.cnblogs.com/netoxi/p/7196891.html
Copyright © 2011-2022 走看看