多线程实操

目录

实操示例

错误方式,不能这样子【看过有人这样加锁】

推荐这种

多线程实现的三种方式

1.实现Runnable接口

2.实现Callable接口,重载call方法

 3.继承 Thread类

线程和线程池


实操示例

错误方式,不能这样子【看过有人这样加锁】

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()方法执行;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值