上次的java多线程问题的拓展
看了一下Java并发编程实战之后,感觉有必要试一试里面的知识点。
上次是直接开启线程,这次尝试换用java.util.concurrent包来实现多线程编程。
一、先上代码
testThread.java
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 63 64 65 66 67 68 69 70 |
package com.xie.testThread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; public class testThread { private static AtomicInteger incrementId = new AtomicInteger(0); public static int getincrementId() { return incrementId.get(); } public void test() { Callable<Integer> handler = new Callable<Integer>() { public Integer call() throws Exception { // TODO Auto-generated method stub int id = getincrementId(); System.out.println(Thread.currentThread().getName() + ":" + id); return id; } }; int num = Runtime.getRuntime().availableProcessors(); ExecutorService executorService = Executors.newFixedThreadPool(num); System.out.println("cpu核数为" + num + ",开启" + (num) + "条线程"); Future<Integer> future = null; while ((getincrementId()) < 1000) { incrementId.incrementAndGet(); future = executorService.submit(handler); try { System.out.println(future.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } executorService.shutdown(); } public static void main(String[] args) { testThread testThread = new testThread(); testThread.test(); } } |
1.一些简单的解释
Callable接口有点类似于开启Thread中的类,需要实现一个call()方法。方法里就写需要并发的逻辑。
这里的:
1 2 3 4 5 6 7 8 9 10 11 12 |
Callable<Integer> handler = new Callable<Integer>() { public Integer call() throws Exception { // TODO Auto-generated method stub int id = getincrementId(); System.out.println(Thread.currentThread().getName() + ":" + id); return id; } }; |
其实等同于新建一个线程类,只不过实现的接口不一样而已:
hander.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.xie.testThread; import java.util.concurrent.Callable; public class handler implements Callable<Integer>{ public Integer call() throws Exception { // TODO Auto-generated method stub int id = testThread.getincrementId(); System.out.println(Thread.currentThread().getName() + ":" + id); return id; } } |
ExecutorService开启一个线程池,通过newFixedThreadPool()方法来指定要开多少条线程(至于开多少条比较适合,下面将会提到)。
Future接口可以用来取Callable执行后的结果。
2.我个人的编程思路如下:
首先确认我们要干什么(要往数据库里插入10000条数据),那么要并发的部分就是插入数据的操作(这里用syso代替)。所以先声明Callable接口,实现call方法,将要并发的逻辑写在里面:
1 2 3 4 5 6 7 8 9 10 11 12 |
Callable<Integer> handler = new Callable<Integer>() { public Integer call() throws Exception { // TODO Auto-generated method stub int id = getincrementId(); System.out.println(Thread.currentThread().getName() + ":" + id); return id; } }; |
因为要保证并发下的线程安全,计数需要使用线程安全类(上次已经提到了)。
1 2 3 4 5 6 |
private static AtomicInteger incrementId = new AtomicInteger(0); public static int getincrementId() { return incrementId.get(); } |
既然需要并发,就要开启一个线程池:
1 |
ExecutorService executorService = Executors.newFixedThreadPool(num + 1); |
因为需要拿到并发时线程执行的结果,所以需要定义一个future接口
1 |
Future<Integer> future = null; |
只要还有数据没插入(小于10000),那么就提交一次handler,由线程池中的线程认领执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
while ((getincrementId()) < 1000) { incrementId.incrementAndGet(); future = executorService.submit(handler); try { System.out.println(future.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
养成良好习惯,执行完毕后关闭线程池(我个人觉得写在finally块中比较好)。
1 |
executorService.shutdown(); |
二、开启多少线程比较好?
这是一个问题,我查了一下,基本上都认为,由具体业务和硬件情况共同决定,没有比较硬性的规定。
但是也有这样的说法:线程数<=cpu数
来自:https://www.zhihu.com/question/28186782
既然需要根据具体机器的cpu数来开启线程,那么就要想办法获取cpu数,java这样获取即可:
1 |
int num = Runtime.getRuntime().availableProcessors(); |
三、总结
java.util.concurrent包值得深究。