前言
前段时间一直在做数据同步的工作,就涉及到数据量比较大,采用多线程来同步,但是地市又没有分表。将一下场景吧。
假设一共有10张表数据要同步,每张表包含10个地市,每张表需要这10个地市的数据都同步完了,然后统一进行一次处理。
开始设想的是,只为每张表启动一个线程,然后这一个线程直接跑完所有地市。结果发现效果很差,同步一次需要大概半小时左右才能将数据同步完,所以就想着改进,给每个张表分配一个主线程,然后在每张表下,每个地市分配一个子线程。这样这张表的所有地市都同步操作,最后注意的是需要等待所有子线程执行完后主线程接着执行后续操作。
这里提供两种方法。
join
通过join 方法,我们知道join() 是等待当前线程执行完成才接着操作。
为了简化,我们创建一个类似的demo,main现程创建两个主线程,每个主线程创建5个子线程。
public class Solution {
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
List<Thread> threads=job(Thread.currentThread().getId());
//让所有子线程执行完
for(Thread td:threads){
try {
td.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程"+Thread.currentThread().getId()+"执行完成");
}
}).start();
}
System.out.println("main 线程"+Thread.currentThread().getId());
}
public static List<Thread> job (long id){
List<Thread> list =new ArrayList<>();
for(int j=0;j<5;j++){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
//让线程号为12 的子线程都休眠1毫秒。一试差别
try {
if(id==12) {
Thread.sleep(2);
}else{
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("父线程"+id+"子线程"+Thread.currentThread().getId()+"执行完成");
}
});
list.add(thread);
thread.start();
}
//arraylist 是线程不安全的。所以使用Collections.synchronizedList方法确保list线程安全,不建议使用vertor,vertor性能不高,属于Java保留方法了,早期使用
return Collections.synchronizedList(list);
}
}
效果如下:
线程池的awaitTermination方法。
我们的子线程可以通过线程池来创建。然后通过线程池的awaitTermination方法,表示等待所有子线程最多多长时间,如果所有子线程都执行完了或者超过了指定的时间还没有执行完,那就执行父线程不再等待。这样做的原因是避免因为子线程处于阻塞状态,导致整个父线程也无法执行。
代码如下:
public class Solution2 {
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
ExecutorService threadPool = Executors.newCachedThreadPool();
long id=Thread.currentThread().getId();
for(int j=0;j<5;j++){
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
if(id==12) {
Thread.sleep(2*1000);
}else{
Thread.sleep(1*1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("父线程"+id+"子线程"+Thread.currentThread().getId()+"执行完成");
}
});
}
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
threadPool.shutdown();
try {
// 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行
// 设置最长等待1秒
threadPool.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getId()+"执行完成");
}
}).start();
}
System.out.println("main 线程"+Thread.currentThread().getId());
}
}
我们这里设置的等待时间为1秒,然后子线程休眠1s 或者2秒看下效果。
可以看到现场12的子线程执行时间超过了限制,父线程不再等待了。
接下来我们再调整一下,修改子线程休眠时间为1或2毫秒,等待最长时间为1000秒
for(int j=0;j<5;j++){
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
if(id==12) {
Thread.sleep(2);
}else{
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("父线程"+id+"子线程"+Thread.currentThread().getId()+"执行完成");
}
});
}
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
threadPool.shutdown();
try {
// 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行
// 设置最长等待10秒
threadPool.awaitTermination(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}