Java 并发编程之测试(二)

资源管理的测试

先摆上昨天测试用的栗子

import java.util.concurrent.Semaphore;

public class BoundedBuffer<E> {

	private final Semaphore availableItems, availableSpaces;
	private final E[] Items;
	private int putPosition = 0, takePosition = 0;

	public BoundedBuffer(int capacity) {
		availableItems = new Semaphore(0);
		availableSpaces = new Semaphore(capacity);
		Items = (E[]) new Object[capacity];
	}

	public boolean isEmpty() {
		return availableItems.availablePermits() == 0;
	}

	public boolean isFull() {
		return availableSpaces.availablePermits() == 0;
	}

	public void put(E x) throws InterruptedException {
		availableSpaces.acquire();
		doInsert(x);
		availableItems.release();
	}

	public E take() throws InterruptedException {
		availableItems.acquire();
		E item = doExtra();
		availableSpaces.release();
		return item;
	}

	private synchronized void doInsert(E x) {
		// TODO Auto-generated method stub
		int i = putPosition;
		Items[i] = x;
		putPosition = (++i == Items.length) ? 0 : i;
	}

	private synchronized E doExtra() {
		int i = takePosition;
		E x = Items[i];
		Items[i] = null;
		takePosition = (++i == Items.length) ? 0 : i;
		return x;
	}

}
对于像Boundedbuffer这样的类来说,资源管理的问题尤为重要。之所以要限制缓存的大小 ,其原因就是要防止由于资源耗尽而导致应用程序发生故障。例如生产者的速度远远高于消费者的速度。

通过一些测量应用程序中内存使用情况 的堆检查 工具,可以很容易地测试出对内存的不合理占用。许多商业和开源的堆分析工具中都支持这种功能 。

import static org.junit.Assert.assertTrue;
import junit.framework.Test;

public class PutTakeTest {
	final int CAPACITY = 1000;

	class Big {
		double[] big = new double[10000];
	}

	public static void main(String[] args) throws InterruptedException {
		new PutTakeTest().testLeak();
	}

	void testLeak() throws InterruptedException {
		BoundedBuffer<Big> bb = new BoundedBuffer<PutTakeTest.Big>(CAPACITY);
		long heapsize1 = Runtime.getRuntime().totalMemory();
		for (int i = 0; i < CAPACITY; i++) {
			bb.put(new Big());
		}
		for (int i = 0; i < CAPACITY; i++) {
			bb.take();
		}
		long heapsize2 = Runtime.getRuntime().totalMemory();
		System.out.println(heapsize1 + "\n" + heapsize2);
	}
}
testLeak方法将多个大型对象插入到一个有界缓存中,然后再将它们移除,第2个堆快照中的内存用量应用与第一个堆快照中的内存用量基本相同,然而,doExtra如果忘记将返回元素的引用置为空(Items[i] = null;)那在两次快照中执行的内存用量将明显不同

不过在我实际测试的过程中,两次内存用量在将元素的引用置为空的条件下还是明显的不同,应该是没有触发system.gc。但是我们无法强制去触发垃圾回收。。所以这个测试在技术上还是有点问题的。可能对选择的垃圾收集器也有要求。我用的是CMS并且设置在内存占用达到75%才触发FullGC。

使用回调

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

import javax.management.MXBean;

import org.junit.experimental.max.MaxCore;

import static org.junit.Assert.*;

public class TestingThreadFactory implements ThreadFactory {
	public final AtomicInteger numCreated = new AtomicInteger();
	private final ThreadFactory factory = Executors.defaultThreadFactory();

	public static void main(String[] args) {
		try {
			new TestingThreadFactory().testPoolExpansion();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public Thread newThread(Runnable r) {
		// TODO Auto-generated method stub
		numCreated.incrementAndGet();
		return factory.newThread(r);
	}

	public void testPoolExpansion() throws InterruptedException {
		int MAX_SIZE = 10;
		TestingThreadFactory threadFactory = new TestingThreadFactory();
		ExecutorService exec = Executors.newFixedThreadPool(MAX_SIZE,
				threadFactory);
		for (int i = 0; i < 10 * MAX_SIZE; i++) {
			exec.execute(new Runnable() {
				public void run() {
					try {
						Thread.sleep(Long.MAX_VALUE);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						Thread.currentThread().interrupt();
					}
				}
			});
		}
		for (int i = 0; i < 20 && threadFactory.numCreated.get() < MAX_SIZE; i++) {
			Thread.sleep(100);
		}
		assertEquals(threadFactory.numCreated.get(), MAX_SIZE);
		System.out.println(threadFactory.numCreated.get());
		exec.shutdown();
	}
}
上面这段代码可以测试线程池的扩展能力。在ThreadPoolExecutor中调用任务的Runnable和ThreadFactory。

在测试线程时,有以下几点,在需要更多的线程时创建新线程,在不需要时不创建 ,以及当需要回收空闲线程时执行回收操作等 。


程序中让线程进行长时间的睡眠是因为测试当把一些运行时间较长的任务提前给线程池时,线程池中的任务数量在长时间内都不会变化,这就可以进行一些判断,例如当测试线程池是否能够按照预期方式进行扩展 。






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值