第八章 Testing Concurrent Applications(测试并发应用)【上】

来源:互联网 发布:js获取自定义属性 编辑:程序博客网 时间:2024/06/12 22:09

本章涉及内容:

  • 监控Lock接口
  • 监控Phaser类
  • 监控一个Executor框架
  • 监控Fork/Join线程池
  • 写有效的日志
  • 用FindBugs分析并发代码
  • 配置Eclipse调试并发代码
  • 用MultithreadTC测试并发代码

1、简介

并发测试的不同的是你很难重现之前的错误或者异常。

2、监控Lock接口

package com.jack;import java.util.Collection;import java.util.concurrent.locks.ReentrantLock;public class MyLock extends ReentrantLock{/** *  */private static final long serialVersionUID = 1L;public String getOwnerName(){if(this.getOwner() == null){return "None";}return this.getOwner().getName();}public Collection<Thread> getThreads(){return this.getQueuedThreads();}}

总结:一个方法getOwnerName()返回当前拥有锁的线程名。如果为null 返回None、getThreads()返回当前等待的线程。

package com.jack;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;public class Task implements Runnable{private Lock lock;public Task(Lock lock) {super();this.lock = lock;}@Overridepublic void run() {for (int i=0; i<5; i++){lock.lock();System.out.printf("%s: 获取这个锁\n", Thread.currentThread().getName());try {TimeUnit.MILLISECONDS.sleep(500);System.out.printf("%s: 释放这个锁\n", Thread.currentThread().getName());}catch (InterruptedException e){e.printStackTrace();} finally{lock.unlock();}}}}

package com.jack;import java.util.Collection;import java.util.concurrent.TimeUnit;public class Main {public static void main(String[] args) throws Exception{MyLock lock = new MyLock();Thread threads[] = new Thread[5];for (int i=0; i<5; i++){Task task = new Task(lock);threads[i] = new Thread(task);threads[i].start();}for (int i=0; i<15; i++){System.out.printf("Main: 打印锁日志\n");System.out.printf("***********************\n");System.out.printf("Lock: 当前锁拥有者:%s\n", lock.getOwnerName());System.out.printf("Lock: 是否有正在等待线程队列: %s\n", lock.hasQueuedThreads());if(lock.hasQueuedThreads()){System.out.printf("Lock: 队列的长度: %d\n", lock.getQueueLength());System.out.printf("Lock: 队列的线程:\n");Collection<Thread> lockedThreads = lock.getThreads();for (Thread lockedThread: lockedThreads){System.out.printf("%s  ", lockedThread.getName());}System.out.println("");}TimeUnit.SECONDS.sleep(1);}}}


总结:创建5个线程测试,每个线程重复获取锁5次

日志:限于篇幅这只是部分日志

Thread-0: 获取这个锁Main: 打印锁日志***********************Lock: 当前锁拥有者:Thread-0Lock: 是否有正在等待线程队列: trueLock: 队列的长度: 4Lock: 队列的线程:Thread-4  Thread-3  Thread-1  Thread-2  Thread-0: 释放这个锁Thread-0: 获取这个锁

扩展:

  • 1、getOwnerName() : 返回获取锁的线程名
  • 2、getThreads() :返回等待的线程
  • 3、hasQueuedThreads() : 是否有线程等待获取锁,有true ,没有false
  • 4、getQueueLength():返回等待获取锁的线程大小
  • 5、isLocked() : 判断有线程获取了锁
  • 6、isFair() : 表示开启公平模式
  • 7、getHoldCount():表示当前线程获取线程的时间
  • 8、isHeldByCurrentThread(): 判断锁是否被当前线程获取

3、监控Phaser类

阶段类,所有的线程必须完成第一阶段才能进入下一阶段。

package com.jack;import java.util.concurrent.Phaser;import java.util.concurrent.TimeUnit;public class Task implements Runnable{private int time;private Phaser phaser;public Task(int time, Phaser phaser) {super();this.time = time;this.phaser = phaser;}@Overridepublic void run() {phaser.arrive();System.out.printf("%s: 进入第一阶段\n", Thread.currentThread().getName());try{TimeUnit.SECONDS.sleep(time);}catch (InterruptedException e){e.printStackTrace();}System.out.printf("%s: 完成第一阶段\n", Thread.currentThread().getName());//开始等待后面的线程phaser.arriveAndAwaitAdvance();System.out.printf("%s: 进入第二阶段\n", Thread.currentThread().getName());try{TimeUnit.SECONDS.sleep(time);}catch(InterruptedException e){e.printStackTrace();}System.out.printf("%s: 完成第二阶段\n", Thread.currentThread().getName());phaser.arriveAndAwaitAdvance();System.out.printf("%s: 进入第三阶段\n", Thread.currentThread().getName());try{TimeUnit.SECONDS.sleep(time);}catch (InterruptedException e){e.printStackTrace();}System.out.printf("%s: 完成第三阶段\n", Thread.currentThread().getName());//放弃等待,结束phaser.arriveAndDeregister();}}

package com.jack;import java.util.concurrent.Phaser;import java.util.concurrent.TimeUnit;public class Main {public static void main(String[] args) throws Exception{//表示注册几个线程Phaser phaser = new Phaser(3);for (int i=0; i<3; i++){Task task = new Task(i+1, phaser);Thread thread = new Thread(task);thread.start();}for (int i=0; i<10; i++){System.out.printf("*********************\n");System.out.printf("Main : 阶段日志\n");System.out.printf("Main: 当前阶段数:%d\n", phaser.getPhase());System.out.printf("Main: 注册线程的数量:%d\n", phaser.getRegisteredParties());System.out.printf("Main : 到达线程数据量:%d\n", phaser.getArrivedParties());System.out.printf("Main: 未达到线程的数量: %d\n", phaser.getUnarrivedParties());System.out.printf("************************\n");TimeUnit.SECONDS.sleep(1);}}}

日志: 部分日志
*********************Main : 阶段日志Thread-0: 进入第一阶段Thread-2: 进入第一阶段Thread-1: 进入第一阶段Main: 当前阶段数:1Main: 注册线程的数量:3Main : 到达线程数据量:0Main: 未达到线程的数量: 3************************Thread-0: 完成第一阶段*********************Main : 阶段日志Main: 当前阶段数:1Main: 注册线程的数量:3Main : 到达线程数据量:1Main: 未达到线程的数量: 2************************Thread-1: 完成第一阶段*********************Main : 阶段日志Main: 当前阶段数:1Main: 注册线程的数量:3Main : 到达线程数据量:2Main: 未达到线程的数量: 1************************

扩展:

  • 1、getPhase() : 返回当前阶段数
  • 2、getRegisteredParties() 返回注册线程的数量
  • 4、getArrivedParties() : 表示到达某个阶段的线程的数量
  • 4、getUnarrivedParties():表示没有到达某个阶段线程的数量

4、监控Executor框架

package com.jack;import java.util.concurrent.TimeUnit;public class Task implements Runnable{private long milliseconds;public Task(long milliseconds) {super();this.milliseconds = milliseconds;}@Overridepublic void run() {System.out.printf("%s: 开始\n", Thread.currentThread().getName());try{TimeUnit.MILLISECONDS.sleep(milliseconds);}catch(InterruptedException e){e.printStackTrace();}System.out.printf("%s:结束\n", Thread.currentThread().getName());}}

package com.jack;import java.util.Random;import java.util.concurrent.Executors;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class Main {public static void main(String[] args) throws Exception{ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();Random random = new Random();for (int i=0; i<10; i++){Task task = new Task( random.nextInt(10000));executor.submit(task);}for (int i=0; i<5; i++){showLog(executor);TimeUnit.SECONDS.sleep(1);}executor.shutdown();for (int i=0; i<5; i++){showLog(executor);TimeUnit.SECONDS.sleep(1);}executor.awaitTermination(1, TimeUnit.DAYS);System.out.printf("Main: 执行完毕\n");}private static void showLog(ThreadPoolExecutor executor) {System.out.printf("*********************************\n");System.out.printf("Main :执行Executor 日志\n");System.out.printf("Main: 线程池允许空闲线程为多少:%d\n", executor.getCorePoolSize());System.out.printf("Main:线程池大小: %d\n", executor.getPoolSize());System.out.printf("Main: 活跃线程数量: %d\n", executor.getActiveCount());System.out.printf("Main: 任务数量:%d\n", executor.getTaskCount());System.out.printf("Main: 完成任务数量:%d\n", executor.getCompletedTaskCount());System.out.printf("Main: 线程池是否关闭:%s\n", executor.isShutdown());System.out.printf("Main: 是否正在终止:%s\n", executor.isTerminating());System.out.printf("Main: 是否已经终止了:%s\n", executor.isTerminated());System.out.printf("*********************************\n");}}

日志:部分日志

pool-1-thread-1: 开始pool-1-thread-10: 开始pool-1-thread-9: 开始*********************************Main :执行Executor 日志Main: 线程池允许空闲线程为多少:0pool-1-thread-8: 开始pool-1-thread-7: 开始pool-1-thread-6: 开始pool-1-thread-5: 开始pool-1-thread-4: 开始pool-1-thread-3: 开始pool-1-thread-2: 开始Main:线程池大小: 10Main: 活跃线程数量: 10Main: 任务数量:10Main: 完成任务数量:0Main: 线程池是否关闭:falseMain: 是否正在终止:falseMain: 是否已经终止了:false******************************************************************Main :执行Executor 日志Main: 线程池允许空闲线程为多少:0Main:线程池大小: 10Main: 活跃线程数量: 10Main: 任务数量:10Main: 完成任务数量:0Main: 线程池是否关闭:falseMain: 是否正在终止:falseMain: 是否已经终止了:false

总结:

  • 1、getCorePoolSize() :允许空闲的线程数量
  • 2、getPoolSize(): 返回实际的线程数量
  • 3、getActiveCount(): 返回当前正在执行的线程数量
  • 4、getTaskCount(): 返回任务的数量
  • 5、getCompletedTaskCount() 返回完成任务的数量
  • 6、isShutDown() : 判断线程池是否关闭(executor.shutdown()方法)
  • 7、isTerminating: 判断executor是否正在终止
  • 8、isTerminated: 判断executor是否已经终止了。

5、监控Fork/Join线程池

package com.jack;import java.util.concurrent.RecursiveAction;public class Task extends RecursiveAction{private int array[];private int start;private int end;public Task(int[] array, int start, int end) {super();this.array = array;this.start = start;this.end = end;}@Overrideprotected void compute() {if(end -start>100){int mid = (start+end)/2;Task task1 = new Task(array, start, mid);Task task2 = new Task(array, mid, end);task1.fork();task2.fork();task1.join();task2.join();} else {for (int i=start; i<end; i++){array[i] ++;try {Thread.sleep(5);} catch (InterruptedException e){e.printStackTrace();}}}}}

package com.jack;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.TimeUnit;public class Main {public static void main(String[] args) throws Exception{ForkJoinPool pool = new ForkJoinPool( );int array[]  = new int[10000];Task task1 = new Task(array,0,array.length);pool.execute(task1);while (!task1.isDone()){showLog(pool);TimeUnit.SECONDS.sleep(1);}pool.shutdown();pool.awaitTermination(1, TimeUnit.DAYS);showLog(pool);System.out.printf("Main:执行完毕了\n");}private static void showLog(ForkJoinPool pool) {System.out.printf("***************************\n");System.out.printf("Main:线程日志\n");System.out.printf("Main:线程池:并行任务%d\n", pool.getParallelism());System.out.printf("Main:线程池工作线程的数量:%d\n", pool.getPoolSize());System.out.printf("Main:激活线程的数量:%d\n", pool.getActiveThreadCount());System.out.printf("Main: 运行并没有阻塞工作线程数量:%d\n", pool.getRunningThreadCount());System.out.printf("Main: 提交任务的数量没有执行:%d\n", pool.getQueuedSubmissionCount());System.out.printf("Main: 队列任务数量已经执行:%d\n", pool.getQueuedTaskCount());System.out.printf("Main: 队列中是否有提交任务未执行:%s\n", pool.hasQueuedSubmissions());System.out.printf("Main: 工作线程窃取其他线程任务的数量:%d\n", pool.getStealCount());System.out.printf("Main: 是否已经终止 %s\n", pool.isTerminated());System.out.printf("*******************************\n");}}

日志:部分日志

***************************Main:线程日志Main:线程池:并行任务4Main:线程池工作线程的数量:4Main:激活线程的数量:4Main: 运行并没有阻塞工作线程数量:0Main: 提交任务的数量没有执行:0Main: 队列任务数量已经执行:38Main: 队列中是否有提交任务未执行:falseMain: 工作线程窃取其他线程任务的数量:0Main: 是否已经终止 false**********************************************************Main:线程日志Main:线程池:并行任务4Main:线程池工作线程的数量:4Main:激活线程的数量:4Main: 运行并没有阻塞工作线程数量:0Main: 提交任务的数量没有执行:0Main: 队列任务数量已经执行:34Main: 队列中是否有提交任务未执行:falseMain: 工作线程窃取其他线程任务的数量:0Main: 是否已经终止 false*******************************