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

本章涉及内容:

  • 监控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);
		}
	}
}


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

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

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
*******************************



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值