本章涉及内容:
日志:
首先将FindBugs控制窗口打开
日志:
总结:
- 监控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() : 控制执行顺序
全书已经完了。。。。