和前同事闲聊小记

前言

这是一篇技术问题讨论文章~~~

最近和前同事(朋友)聊天,帮他分析了这么一个需求,蛮有意思的,特来分享下其代码。

和他聊了很久,算是大致上听懂了他的需求,总结下他的需求:

假设有若干方法(任务)(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{
//获取指定class的public方法
Method[] methods=ThingsMethod.class.getDeclaredMethods();
//有多少个方法就创建多少个线程池,一个方法指定一个定长线程池
for(Method method:methods){
ExecutorService pool= Executors.newFixedThreadPool(1);
poolMap.put(method.getName(),pool);
}
}
//list里面假设是要执行的任务,变化的
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事情开始----->");
//假设做A事情花费1s
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事情开始----->");
//假设做B事情花费3s
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事情开始----->");
//假设做C事情花费2s
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事情开始----->");
//假设做D事情花费5s
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事情开始----->");
//假设做A事情花费1s
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事情开始----->");
//假设做B事情花费3s
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事情开始----->");
//假设做C事情花费2s
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事情开始----->");
//假设做D事情花费5s
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("线程"+id+"做D事情结束----->");
return true;
}
}

由于在主线程上开启了多线程,所以这种方法观看日志比较不方便。这种方法的正确性也还没来得及验证。

如果大家有什么好的其他想法也可以讨论交流。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道