目录
实操示例
错误方式,不能这样子【看过有人这样加锁】
public class DataMigrateApplication {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
//启用线程池
Task task = new Task(sceneId, fileDao, obsUtil, fastDfsUtil);
exec.execute(task);
exec.execute(task);
exec.execute(task);
exec.shutdown();//线程停止,拒绝新的队列加入
while (true) {
if (exec.isTerminated()) {//判断是否真正的中止,即队列是否工作完毕
lock.unlock();
}
}
} catch (Exception e) {
System.out.println("线程内异常错误:" + e.getMessage());
} finally {
lock.unlock();
System.out.println("正常释放锁");
}
}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Task implements Runnable {
public Task() {}
@Override
public void run() {
try {
//业务
} catch (Exception e) {
logger.error("error-----task失败:" + e.getMessage());//obs上传失败
e.printStackTrace();
}
}
}
这种方式首先锁在最外面加锁会没用,实际有几个进程就insert几次,根本锁不住;
其次在双重释放的时候会造成null异常
推荐这种
public class DataMigrateApplication {
public static void main(String[] args) {
try {
// java -jar -DsceneId=9999 "D:\code\filestore\target\filestore-0.0.1-SNAPSHOT.jar"
String sceneId = System.getProperty("sceneId");
System.out.println("需要提前备份数据,若没备份,请立即ctrl+c停止程序,输入指定sceneId:" + sceneId);
//开始启动项目
SpringApplication.run(DataMigrateApplication.class, args);
IFileDao fileDao = BeanContext.getApplicationContext().getBean(IFileDao.class);
HuaWeiObsUtil obsUtil = BeanContext.getApplicationContext().getBean(HuaWeiObsUtil.class);
FastDfsUtil fastDfsUtil = BeanContext.getApplicationContext().getBean(FastDfsUtil.class);
ExecutorService exec = Executors.newFixedThreadPool(3);
//启用线程池
Task task = new Task(sceneId, fileDao, obsUtil, fastDfsUtil);
exec.execute(task);
exec.execute(task);
exec.execute(task);
exec.shutdown();
// Thread aa = new Thread(task);
// Thread bb = new Thread(task);
// Thread cc = new Thread(task);
// aa.start();
// bb.start();
// cc.start();
} catch (BeansException e) {
System.out.println("异常错误:" + e.getMessage());
e.printStackTrace();
}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Task implements Runnable {
private Lock lock = new ReentrantLock();
public Task() {}
@Override
public void run() {
try {
lock.lock();
//业务
} catch (Exception e) {
logger.error("error-----task失败:" + e.getMessage());//obs上传失败
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
以上为处理历史数据的demo,后续是以jar包形式处理的
多线程实现的三种方式
1.实现Runnable接口
public class TestTask implements Runnable {
// 在该方法中实现线程的任务处理逻辑
@Override
public void run() {
System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName());
}
}
2.实现Callable接口,重载call方法
public class TestTask implements Callable {
@Override
public Integer call(){
return 0;
}
}
3.继承 Thread类
public class TestTask extends Thread {
@Override
public void run() {
}
}
//总结:线程开启不一定立即执行,由cpu调度执行
①和②的区别在于:
runnable没有返回值,callable有返回值;
runable被线程run异步调用,callable被Future的run方法调用,非异步;
线程和线程池
为了避免重复创建和销毁进程,有了线程池。
实质上是:从线程池获取空闲线程,干完活儿后,归还线程到线程池。
缺点:造成oom异常
@SpringBootApplication
public class MoreApplication {
public static void main(String[] args) {
// 继承Thread类
// Thread welcomeThread = new WelcomeThread();
Thread aa = new Thread(new TestTask());
aa.start();//注意,这里的start是调用线程,而非run,run为调用此方法
System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName());
}
}
ExecutorService exec = Executors.newFixedThreadPool(3);
//启用线程池
Task task = new Task();
exec.execute(task);
exec.execute(task);
exec.execute(task);
exec.shutdown();//线程停止,拒绝新的队列加入
while (true) {
if (exec.isTerminated()) {//判断是否真正的中止,即队列是否工作完毕
//这里只会执行一次
System.out.printf("只执行一次")
}
}
线程池中execute和submit区别:
execute执行的是runnable任务,submit执行runnable和callable;
execute没有返回值,而submit会返回Future类型的对象;
execute若有异常会抛出,而submit不会抛出,直到调用Future.get()才会抛出;
总结
runnable 若线程执行,核心方法是run();
若程序池ExecuteService执行,通过execuse()方法执行;
callable 若线程执行,核心方法是call();
若程序池ExecuteService执行,通过submit()方法执行;