本章涉及内容:
- 监控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;
}
@Override
public 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);
}
}
}
日志:限于篇幅这只是部分日志
Thread-0: 获取这个锁
Main: 打印锁日志
***********************
Lock: 当前锁拥有者:Thread-0
Lock: 是否有正在等待线程队列: true
Lock: 队列的长度: 4
Lock: 队列的线程:
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;
}
@Override
public 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: 当前阶段数:1
Main: 注册线程的数量:3
Main : 到达线程数据量:0
Main: 未达到线程的数量: 3
************************
Thread-0: 完成第一阶段
*********************
Main : 阶段日志
Main: 当前阶段数:1
Main: 注册线程的数量:3
Main : 到达线程数据量:1
Main: 未达到线程的数量: 2
************************
Thread-1: 完成第一阶段
*********************
Main : 阶段日志
Main: 当前阶段数:1
Main: 注册线程的数量:3
Main : 到达线程数据量:2
Main: 未达到线程的数量: 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;
}
@Override
public 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: 线程池允许空闲线程为多少:0
pool-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:线程池大小: 10
Main: 活跃线程数量: 10
Main: 任务数量:10
Main: 完成任务数量:0
Main: 线程池是否关闭:false
Main: 是否正在终止:false
Main: 是否已经终止了:false
*********************************
*********************************
Main :执行Executor 日志
Main: 线程池允许空闲线程为多少:0
Main:线程池大小: 10
Main: 活跃线程数量: 10
Main: 任务数量:10
Main: 完成任务数量:0
Main: 线程池是否关闭:false
Main: 是否正在终止:false
Main: 是否已经终止了: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;
}
@Override
protected 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:线程池:并行任务4
Main:线程池工作线程的数量:4
Main:激活线程的数量:4
Main: 运行并没有阻塞工作线程数量:0
Main: 提交任务的数量没有执行:0
Main: 队列任务数量已经执行:38
Main: 队列中是否有提交任务未执行:false
Main: 工作线程窃取其他线程任务的数量:0
Main: 是否已经终止 false
*******************************
***************************
Main:线程日志
Main:线程池:并行任务4
Main:线程池工作线程的数量:4
Main:激活线程的数量:4
Main: 运行并没有阻塞工作线程数量:0
Main: 提交任务的数量没有执行:0
Main: 队列任务数量已经执行:34
Main: 队列中是否有提交任务未执行:false
Main: 工作线程窃取其他线程任务的数量:0
Main: 是否已经终止 false
*******************************