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

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

1、写有效的日志信息

利用java.util.logging包为你并发应用添加日志信息

package com.jack;

import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;

/**
 * 定义日志格式
 * @author Administrator
 *
 */
public class MyFormatter extends Formatter{

	@Override
	public String format(LogRecord record) {
		StringBuilder sb = new StringBuilder();
		sb.append("[" + record.getLevel()+"] -");
		sb.append(new Date(record.getMillis()) + " : ");
		sb.append(record.getSourceClassName() + "." + record.getSourceMethodName() + " : ");
		sb.append(record.getMessage() + "\n");
		return sb.toString();
	}

}

package com.jack;

import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 日志处理器,并将格式设置为自定义格式
 * @author Administrator
 *
 */
public class MyLogger {
	/**
	 * 
	 */
	private static Handler handler;
	public static Logger getLogger(String name){
		Logger logger = Logger.getLogger(name);
		logger.setLevel(Level.ALL);
		try {
			if(handler ==null){
				handler = new FileHandler("recipe8.log");
				Formatter format = new MyFormatter();
				handler.setFormatter(format);
			}
			if(logger.getHandlers().length==0){
				logger.addHandler(handler);
			}
		} catch (SecurityException e){
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return logger;
	}
}

package com.jack;

import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class Task implements Runnable{

	
	@Override
	public void run() {
		Logger logger =MyLogger.getLogger(this.getClass().getName());
		logger.entering(Thread.currentThread().getName(),"run()");
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		
		logger.exiting(Thread.currentThread().getName(), "rum()", Thread.currentThread());
	}



}

package com.jack;

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
	public static void main(String[] args) throws Exception{
		Logger logger = MyLogger.getLogger("Core");
		logger.entering("Core", "Main()", args);
		Thread threads[] = new Thread[5];
		for (int i=0; i<threads.length; i++){
			logger.log(Level.INFO, "启动线程:" + i);
			Task task = new Task();
			threads[i] = new Thread(task);
			logger.log(Level.INFO, "线程创建:"+ threads[i].getName());
			threads[i].start();
		}
		logger.log(Level.INFO, "十个线程已经创建"+"   等待它们完成");
		for (int i=0; i<threads.length; i++){
			try {
				threads[i].join();
				logger.log(Level.INFO, "线程已经完成了"+threads[i]);
				
			} catch (InterruptedException e){
				logger.log(Level.SEVERE, "Exception", e);
			}
			
		}
		logger.exiting("Core", "main");
	}
}

日志:


扩展:

  • getLevel() : 返回消息级别
  • getMIllis() 返回一个信息发给日志对象的日期
  • getSourceClassName() : 返回发给日志对象的类名
  • getSourceMessageName():  返回一个发送消息的方法名
  • getMessage() : 返回日志消息
  • entering() : 用FINER级别写入信息,表明一个方法已经执行
  • exiting(): 用FINER级别写入信息,表明一个方法已经结束
  • log() : 采用特定的级别写入信息。

写日志注意两点:

  • 1、写必要日志信息
  • 2、日志必须有对应的级别

2、采用FindBugs分析并发代码

检查bug 的工具有Checkstyle, PMD , 或FindBug

FindBugs有独立程序,也有eclipse插件,咱们采用eclipse插件、

首先安装FindBugs插件



安装后重启eclipse



写代码:

package com.jack;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Task implements Runnable{

	private ReentrantLock lock;
	
	public Task(ReentrantLock lock) {
		super();
		this.lock = lock;
	}


	@Override
	public void run() {
		lock.lock();
		try{
			TimeUnit.SECONDS.sleep(1);
			lock.unlock();
		} catch (InterruptedException e){
			e.printStackTrace();
		}
	}



}
package com.jack;

import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
	public static void main(String[] args) throws Exception{
		ReentrantLock lock = new ReentrantLock();
		int num=1;
		for (int i=0; i<10; i++){
			Task task = new Task(lock);
			Thread thread = new Thread(task);
			thread.run();
		}
		if(num!=1) {
			System.out.printf("不会进入");
		}
		//运行时异常它不会检测出来的,所以是静态的
		int i = 1/0;
		System.out.println(i);
	}
}

首先将FindBugs控制窗口打开




最后的效果是:



运行:



效果:



3、配置eclipse调试并发代码


这样你就可以调试某个线程的执行。

4、采用MultithreadedTC 测试并发代码

MultithreadedTC 是用来测试并发应用的jar库,它目的就是解决多线程执行的不确定性。你不能控制执行顺序,为了这个目的,它内部一个节拍器去控制不同线程的执行顺序。

例子:将会学习用MultithreadedTC测试LinkedTransferQueue。

下载googlejar :MultithreadedTC

下载JUnit 4.1 :JUnit 4.1

然后添加到类路径上。(buildpath)

如果是maven工程:添加依赖

<dependency>
			<groupId>com.googlecode.multithreadedtc</groupId>
			<artifactId>multithreadedtc</artifactId>
			<version>1.01</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.1</version>
			<scope>test</scope>
		</dependency>

package learn;



import java.util.concurrent.LinkedTransferQueue;

import edu.umd.cs.mtc.MultithreadedTestCase;

public class ProducerConsumerTest  extends MultithreadedTestCase{
	private LinkedTransferQueue<String> queue;

	@Override
	public void initialize() {
		super.initialize();
		queue = new LinkedTransferQueue<String>();
		System.out.printf("测试:测试已经初始化了\n");
	}
	
	public void thread1() throws InterruptedException {
		String ret = queue.take();
		System.out.printf("线程1: %s\n", ret);
	}
	
	public void thread2() throws InterruptedException {
		waitForTick(1);
		String ret = queue.take();
		System.out.printf("线程2 : %s \n", ret);
	}

	public void thread3() throws InterruptedException {
		waitForTick(1);
		waitForTick(2);
		queue.put("事件1");
		queue.put("事件2");
		System.out.printf("线程3: 插入两个元素\n");
	}
	public void finish(){
		super.finish();
		System.out.printf("测试:结束\n");
		assertEquals(true, queue.size()==0);
		System.out.printf("测试:结果:这个队列已经空了\n");
	}
}

package learn;



import edu.umd.cs.mtc.TestFramework;

public class Main {
	public static void main(String[] args) throws Throwable{
		
		ProducerConsumerTest test = new ProducerConsumerTest();
		System.out.printf("Main: 开始测试\n");
		TestFramework.runOnce(test);
		System.out.printf("Main 测试已经完成\n");
		
	}
}

日志:

Main: 开始测试
测试:测试已经初始化了
线程3: 插入两个元素
线程1: 事件1
线程2 : 事件2 
测试:结束
测试:结果:这个队列已经空了
Main 测试已经完成

总结:

  • 1、initialize():初始化
  • 2、finish(): 执行结束,可以做一些释放资源的事情
  • 3、waitForTick() : 控制执行顺序

全书已经完了。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值