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

来源:互联网 发布:通讯录恢复软件 编辑:程序博客网 时间:2024/06/07 14:30
本章涉及内容:
  • 监控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{@Overridepublic 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{@Overridepublic 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;}@Overridepublic 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;@Overridepublic 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() : 控制执行顺序

全书已经完了。。。。

原创粉丝点击