前言
这是一篇技术问题讨论文章~~~
最近和前同事(朋友)聊天,帮他分析了这么一个需求,蛮有意思的,特来分享下其代码。
和他聊了很久,算是大致上听懂了他的需求,总结下他的需求:
假设有若干方法(任务)(A,B,C,D,E…….),对于每个方法(任务),同一时间只能有一个用户(线程)访问,现一些用户需要执行若干任务(如用户1执行(ABC),用户2执行(BEF),用户3执行(ACEF)….),对每个用户,用户的任务不要求执行顺序,如何尽可能的提高程序运行的效率。
分析
其实这之中比较重要的一点是每个方法同一时间只允许一个用户访问,我们如果给每个方法加上synchronized关键字呢?我从这方面入手没有想到太好的方法。我用了一种方法,就是从队列入手,对每个方法,可以设置一个等待队列,如果有任务正在执行该方法,则将其他的访问该方法的线程挂起,当然,对于每个用户,应该开多个线程去异步执行各个方法。
于是我想到了定长线程池,允许一个线程执行,其他线程进入后会放到等待队列中。对于方法,每有一个方法(任务),就创建一个定长线程池。应当在初始化时就将数据设置好。
代码
以下是我的一些实现代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class DoTask { private static Map<String,ExecutorService> poolMap=new HashMap<>(); static{ Method[] methods=ThingsMethod.class.getDeclaredMethods(); for(Method method:methods){ ExecutorService pool= Executors.newFixedThreadPool(1); poolMap.put(method.getName(),pool); } } public void doTask(List<String> list){ for(String taskStr:list){ final String id="Thread"+Thread.currentThread().getId(); switch (taskStr){ case "A": try{ executeMethod("doSomethingA",id); }catch(Exception e){ e.printStackTrace(); } break;
case "B": try{ executeMethod("doSomethingB",id); }catch(Exception e){ e.printStackTrace(); } break; case "C": try{ executeMethod("doSomethingC",id); }catch(Exception e){ e.printStackTrace(); } break; case "D": try{ executeMethod("doSomethingD",id); }catch(Exception e){ e.printStackTrace(); } break; default: break; }
} } public void executeMethod(String methodName,String id){ poolMap.get(methodName).execute(()->{ try{ Method method=ThingsMethod.class.getMethod(methodName,String.class); method.invoke(ThingsMethod.class.newInstance(),id); }catch(Exception e){ e.printStackTrace(); } }); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ThingsMethod { public boolean doSomethingA(String id){ System.out.println("线程"+id+"做A事情开始----->"); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做A事情结束----->"); return true; } public boolean doSomethingB(String id){ System.out.println("线程"+id+"做B事情开始----->"); try{ Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做B事情结束----->"); return true; } public boolean doSomethingC(String id){ System.out.println("线程"+id+"做C事情开始----->"); try{ Thread.sleep(2000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做C事情结束----->"); return true; } public boolean doSomethingD(String id){ System.out.println("线程"+id+"做D事情开始----->"); try{ Thread.sleep(5000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做D事情结束----->"); return true; } }
|
编写测试类对其进行测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class MyTest implements Runnable{ @Override public void run() { List<String> s=new ArrayList<>(); String [] strings=new String[]{"A","B","C","D"}; int l=(int)Math.ceil(Math.random()*strings.length); for(int i=0;i<l;i++){ s.add(strings[i]); } System.out.println(); System.out.print("Thread"+Thread.currentThread().getId()+"随机生成的任务--》"); for(int i=0;i<s.size();i++){ System.out.print(s.get(i)); } System.out.println(); DoTask doTask=new DoTask(); doTask.doTask(s); } public static void main(String[] args) { MyTest test = new MyTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); Thread t3 = new Thread(test); Thread t4 = new Thread(test); Thread t5 = new Thread(test); Thread t6 = new Thread(test); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); } }
|
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| Thread11随机生成的任务--》A
Thread13随机生成的任务--》A
Thread15随机生成的任务--》AB
Thread14随机生成的任务--》ABCD
Thread12随机生成的任务--》ABCD
Thread16随机生成的任务--》A 线程Thread16做A事情开始-----> 线程Thread14做C事情开始-----> 线程Thread12做B事情开始-----> 线程Thread12做D事情开始-----> 线程Thread16做A事情结束-----> 线程Thread13做A事情开始-----> 线程Thread14做C事情结束-----> 线程Thread12做C事情开始-----> 线程Thread13做A事情结束-----> 线程Thread12做A事情开始-----> 线程Thread12做B事情结束-----> 线程Thread14做B事情开始-----> 线程Thread12做A事情结束-----> 线程Thread14做A事情开始-----> 线程Thread12做C事情结束-----> 线程Thread14做A事情结束-----> 线程Thread11做A事情开始-----> 线程Thread12做D事情结束-----> 线程Thread14做D事情开始-----> 线程Thread11做A事情结束-----> 线程Thread15做A事情开始-----> 线程Thread14做B事情结束-----> 线程Thread15做B事情开始-----> 线程Thread15做A事情结束-----> 线程Thread15做B事情结束-----> 线程Thread14做D事情结束----->
|
可以看到运行结果是正确的。
讨论
我还想到一种就是对各个方法(任务)添加synchronized关键字,这些方法对用户共享,这样每当有用户请求时,开线程池执行各个方法。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class DoTask { private static ThingsMethod method=new ThingsMethod(); public void doTask(List<String> list){ for(String taskStr:list){ ExecutorService executor= Executors.newCachedThreadPool(); executor.execute(()->{ final String id="Thread"+Thread.currentThread().getId(); doSomething(taskStr,id,method); }); } } public void doSomething(String taskStr,String id,ThingsMethod method){ switch (taskStr){ case "A": method.doSomethingA(id); break; case "B": method.doSomethingA(id); break; case "C": method.doSomethingA(id); break; case "D": method.doSomethingA(id); break; default: break; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ThingsMethod { public synchronized boolean doSomethingA(String id){ System.out.println("线程"+id+"做A事情开始----->"); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做A事情结束----->"); return true; } public synchronized boolean doSomethingB(String id){ System.out.println("线程"+id+"做B事情开始----->"); try{ Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做B事情结束----->"); return true; } public synchronized boolean doSomethingC(String id){ System.out.println("线程"+id+"做C事情开始----->"); try{ Thread.sleep(2000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做C事情结束----->"); return true; } public synchronized boolean doSomethingD(String id){ System.out.println("线程"+id+"做D事情开始----->"); try{ Thread.sleep(5000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("线程"+id+"做D事情结束----->"); return true; } }
|
由于在主线程上开启了多线程,所以这种方法观看日志比较不方便。这种方法的正确性也还没来得及验证。
如果大家有什么好的其他想法也可以讨论交流。