前言
最近心境不是很好,然后就开始回忆曾经大学的韶光,好想和大学的朋友再吃一顿饭啊。
然后想到一个月黑风高的晚上,我的宿友和我讨论多线程的问题:
“假如有一个非常大的文件,要入库,你怎样将这个文件读取入库?”
其时我说:“很简单啊,就这样读文件然后入库啊。”
他说:“我其时也是这样说的,然后面试官叫我回去等通知。”
想想还是太菜了,然后他和我说能够用多线程去操作。我其时由于技能原因不知道怎样操作,只知道大约思路。而今日也是有空去实现其时的遗憾了。
思路
思路也挺简单的,便是多条线程共同操作一个变量,这个变量记载了这个大文件读取的进展,也能够理解为文件行数。各个线程读取必定的数据然后记载mark,分段入库。
代码
餐前甜品
此处模仿从数据库读取大量数据原理和分批刺进相同,每条线程循环500次,偏移量为500条,使用AtomicInteger来进行线程变量同享。
public class myThread implements Runnable {
private volatile int num = 0;
private AtomicInteger val = new AtomicInteger();
@Override
public void run() {
for (int i = 0; i < 500; i++) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("select id,name from user limit ");
stringBuffer.append(val.get());
val.addAndGet(500);
stringBuffer.append(",").append(val.get());
System.out.println(stringBuffer);
}
}
}
public static void main(String[] args) {
myThread myRunnable1 = new myThread();
Thread thread1= new Thread(myRunnable1);
Thread thread2 = new Thread(myRunnable1);
thread1.start();
thread2.start();
}
每条线程是500×500,也便是250000,此处开了2条线程,终究结果应该是500000。控制台输出如下
ok,不错,证明方向是正确的,接下来是模仿读取大文件入库。
正餐
线程类
@Component
public class myNewThread extends Thread {
@Autowired
private IUserService userService;
private static AtomicInteger val = new AtomicInteger();
public myNewThread(){
}
public void run() {
List<User> list = new ArrayList<>();
for (int i = 0; i < 500; i++) {
User user = User.builder().wxName(val.get() + "").build();
SoftReference<User> stringBufferSoftReference = new SoftReference<>(user);
list.add(stringBufferSoftReference.get());
val.incrementAndGet();
}
userService.saveBatch(list);
}
}
@Test
void contextLoads() throws InterruptedException {
Thread thread3 = new myNewThread();
Thread thread4 = new myNewThread();
thread3.start();
thread4.start();
}
每条线程刺进400条数据,wxName这个字段记载第几条数据 发动后发现控制台什么都没输出,数据库也没刺进数据,很纳闷。然后通过一番思考后找出来问题所在。 在线程发动后加入以下两行代码
thread3.join();
thread4.join();
原因是,主线程在创立这两个线程后就结束了,子线程还没来得及操作数据库主线程就已经逝世了导致子线程被迫停止。
join表明主等待这线程履行结束,这姿态就不会出现主线程创立完子线程就逝世导致子线程都没来得及履行线程体就死了的状况。
ok!发动!! 坏啦,空指针异常
通过调试发现,是service类在线程里面为空,导致能空指针异常,看来是spring捕获不到线程体。
既然捕获不到那我就传一个给线程体吧。
修改后的线程体
@Component
public class myNewThread extends Thread {
private IUserService userService;
private static AtomicInteger val = new AtomicInteger();
public myNewThread(IUserService userService){
this.userService = userService;
}
public myNewThread(){
}
public void run() {
List<User> list = new ArrayList<>();
for (int i = 0; i < 400; i++) {
User user = User.builder().wxName(val.get() + "").build();
SoftReference<User> stringBufferSoftReference = new SoftReference<>(user);
list.add(stringBufferSoftReference.get());
val.incrementAndGet();
}
userService.saveBatch(list);
}
}
修改后的单元测试
@Autowired
private IUserService userService;
@Test
void contextLoads() throws InterruptedException {
Thread thread3 = new myNewThread(userService);
//Thread thread4 = new myNewThread();
Thread thread4 = new myNewThread(userService);
thread3.start();
thread4.start();
thread3.join();
thread4.join();
}
简单来说便是使用构造函数给线程体传递一个非空的service类。
发动单元测试
证明是能够的刚刚好800条数据
总结
发动线程时要注意主线程和子线程的联系,然后操作数据库时,要注意传入的类是否空指针。
分段刺进就到这儿了,在高并发的状况下还没测试过,明日再说吧,已经是下午5点58了,下班!!!