在开始之前我想纠正一个错误,网上的错误。
网上说多线程数要与cpu数有一定规律,比如说什么最佳线程数应该等于核数*2等说法,对于这种说法
我做了测试,我是这么测试的
第一次10个线程处理2000条数据(调用接口),结果:数据一般;
第二次50个线程处理2000条数据(调用接口),结果:数据快了;
第三次100个线程处理2000条数据(调用接口),结果:数据更更快了,基本上几秒钟完事。
第四次200个线程处理2000条数据(调用接口),结果:数据更更更快了;基本上一下子完事。
就上面的测试结果,我感觉网上说的冒失不对啊,如果只考虑处理速度不考虑其他问题的话,我觉得线程足够多就行,与cpu什么的没什么关系啊(可能,我测试的有问题,望高手给与指点)
另外,以前我对多线程同步总是不知道什么情况下需要去做同步加锁处理,在经过鄙人的反复测试后,个人认为:只有在多个线程共享一个static的资源时才有必要考虑同步,那问题又来了,
既然有大量并发并且加了锁,那就要考虑死锁问题,这里使用的是notify
完成的功能很简单:
每1分钟去一个表里查询list数据并放入java的queue队列里(不是MQ,就是一个java队列)。
然后唤起线程池,启动10个线程来取出queue里的数据并打印出来。
1.监听器
(1)web.xml里
<listener>
<listener-class>com.sf.partner.mgt.context.APIListener</listener-class>
</listener>
(2)代码
public class APIListener extends HttpServlet implements ServletContextListener {
private static final long serialVersionUID = 4401296288922971302L;
public APIListener() {
}
private java.util.Timer timer = null;//定义一个定时器
public void contextInitialized(ServletContextEvent event) {
timer = new java.util.Timer(true);
//TaskQuery自定义定时器,查询list并放入queue队列里
timer.schedule(new TaskQuery(event.getServletContext()),
0, 60*1000);// 1000毫秒*60=1分钟
event.getServletContext().log("已经添加任务调度表-定时器");
}
public void contextDestroyed(ServletContextEvent event) {
timer.cancel();
event.getServletContext().log("定时器销毁");
}
}
2.TaskQuery代码:
public class TaskQuery extends TimerTask {
public static Queue<Object> queue;//Queue是java自己的队列List,具体可看API,是同步安全的.
//这里在介绍一个很好用又强大的线程安全的Map==》ConcurrentHashMap
(具体可看API)
static {
queue = new ConcurrentLinkedQueue<Object>();
}
private static boolean isRunning = false;
private ServletContext context = null;
private String lock1 = "";
public TaskQuery() {
}
public TaskQuery(ServletContext context) {
this.context = context;
}
public void run() {
synchronized (lock1) {
if (!isRunning) {
isRunning = true;
context.log("开始执行查询并放入queue队列");
queryList();
context.log("查询并放入queue队列结束");
ThreadPool.createThreadPool();//唤起线程池
isRunning = false;
} else {
context.log("上一次任务执行还未结束");
}
lock1.notify();//必须要加这个,不然有时会死锁
}
}
/*
*queue 和 这个queryList()方法都可以根据具体业务提取成单独的java文件
*/
public void queryList() {
//list就是从数据库里查询的结果
for(String str : list) {
queue.offer(str);//向队列添加数据
}
}
}
3.ThreadPool线程池代码:
(我以前也没搞过多线程,这个是我在网上看的,
然后自己改改,测了下觉得还行,但是肯定是菜鸟的手笔,说以欢迎大家拍砖) (这里需搞清楚线程对象与线程的区别):
public class ThreadPool {
public static void createThreadPool() {
Random random = new Random();
/**
* 产生一个 ExecutorService 对象,这个对象带有一个大小为 poolSize 的线程池, 若任 务数量大于 poolSize,任务会被放在一个 queue 里顺序执行。这里开启10个线程对象,放入线程池
*/
ExecutorService executor = Executors.newFixedThreadPool(10);
int waitTime = 500;
if (TaskQuery.queue.size() > 0) {
//这个for循环里的代码就是从线程池里获得线程对象,并执行多线程操作
for (int i = 0; i < TaskQuery.queue.size() + 2; i++) {
int time = random.nextInt(1000);
waitTime += time;
Runnable runner = new ExecutorThread(TaskQuery.queue.poll(),
time);
executor.execute(runner);
}
}
try {
executor.shutdown();// 只是将线程池置于关闭状态,不接受新任务,对正在运行的任务不影响
executor.awaitTermination(waitTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
}
}
}
/*
*线程类实现Runnable接口
*/
class ExecutorThread implements Runnable {
private Object obj;
private int delay;
public ExecutorThread(Object obj, int delay) {
this.obj = obj;
this.delay = delay;
}
public void run() {
/*
*这里就是线程需要处理的业务了,可以换成自己的业务
*/
try {
if(obj != null) {
System.out.println("运行的线程数"+Thread.activeCount());
System.out.println((obj.toString());
}
Thread.sleep(delay);
} catch (InterruptedException ignored) {
}
}
}
到这里这个小例子我算写完了,希望大家拍砖指教,希望,千万别出什么幺蛾子啊