Java#34(多线程)

news2024/11/14 20:59:47

目录

线程(thread): 是一个程序内部的一条执行路经(之前写的代码中,main方法的执行就是一条单独的执行路径) 

单线程: 一个程序只有一条执行路线

多线程: 指从软硬件上实现多条执行流程的技术

一.创建多线程

1.继承Thread类

2.实现Runnable接口

3.利用Callable, FutureTask接口实现

二.Thread的常用方法 

1.Thread的构造方法

2.Thread获取和设置线程名称

3.Thread类获得当前线程的对象

三.线程安全

线程安全: 多个线程同时操作一个共享资源的时候可能出现业务安全问题

四.线程同步

加锁: 让多个线程实现先后依次访问共享资源

1.同步代码块

2.同步方法

3.Lock锁

五.线程通信

什么是线程通信、如何实现?

线程通信常见模型

六.线程池

1.线程池就是一个可以复用线程的技术

2.线程池常见面试题

3.ExecutorService的常用方法

4.Executors得到线程池对象的常用方法


线程(thread): 是一个程序内部的一条执行路经(之前写的代码中,main方法的执行就是一条单独的执行路径) 

单线程: 一个程序只有一条执行路线

多线程: 指从软硬件上实现多条执行流程的技术

一.创建多线程

1.继承Thread类

步骤:

(1)定义一个子类继承Thread,然后重写run()方法

(2)创建子类的对象

(3)调用子类对象的start()方法启动线程(启动后执行的是run()方法)

代码示范: 第一段是子类的代码, 第二段是测试类的代码

//1.定义一个子类继承Thread,重写run方法
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程"+i);
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        //2.创建子线程对象
        MyThread mt = new MyThread();
        //3.调用start()方法启动子线程(启动后执行的是run()方法)
        mt.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程"+i);
        }
    }
}

结果展示: (主线程和子线程的执行没有先后之分)(要将主线程的执行任务放在子线程之后,)

用继承Thread类创建多线程的优点和缺点

优点: 编程简单

缺点: 线程类已经继承Thread类, 无法继承其他类

2.实现Runnable接口

步骤:

(1)定义一个线程任务类实现Runnable接口, 重写run( )方法

(2)创建任务对象

(3)将任务对象交给Thread处理表

(3)调用线程对象的start( )方法启动线程

代码示范: 第一段是类的代码,第二段是测试的代码

//创建多线程:子类实现Runnable接口
public class MyThread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程"+i);
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        /*第一种写法//创建任务对象,任务对象不是Thread的子类,无法直接调用start()方法
        MyThread1 mt = new MyThread1();
        //把任务对象交给Thread处理
        Thread t1 = new Thread(mt);
        //调用start()方法
        t.start();*/
        /*第二种写法
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程"+i);
                }
            }
        });
        t2.start();*/
        //第三种写法
        Thread t3 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程" + i);
            }
        });
        t3.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程" + i);
        }
    }
}

结果展示: 

优点: 线程任务类只是实现接口, 可以继续继承类实现接口,扩展性强

缺点: 编程要多一层对象包装, 如果线程有执行结果无法直接返回

3.利用Callable, FutureTask接口实现

步骤:

(1)定义类实现Callable接口,重写call方法, 封装要做的事

(2)用FutureTask把Callable对象封装成线程任务对象

(3)把线程任务对象交给Thread处理

(4)调用Thread的start方法启动线程

(5)线程执行完毕后, 通过FutureTask的get方法获取执行结果

代码示范: 第一段是类的代码, 第二段是测试的代码

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "结果是"+sum;
    }
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //子线程1的代码
        //创建任务类对象
        MyCallable call1 = new MyCallable(50);
        //用FutureTask把Callable对象封装成线程任务对象
        FutureTask<String> f1 = new FutureTask<>(call1);
        //把线程对象交给Thread处理
        Thread t1 = new Thread(f1);
        //通过调用start方法启动线程
        t1.start();
        //线程执行完毕后,通过FutureTask的get方法获取任务执行结果
        String s1 = f1.get();
        System.out.println("子线程1的"+s1);
        //子线程2的代码
        MyCallable call2 = new MyCallable(100);
        //用FutureTask把Callable对象封装成线程任务对象
        FutureTask<String> f2 = new FutureTask<>(call2);
        //把线程对象交给Thread处理
        Thread t2 = new Thread(f2);
        //通过调用start方法启动线程
        t2.start();
        //线程执行完毕后,通过FutureTask的get方法获取任务执行结果
        String s2 = f2.get();
        System.out.println("子线程2的"+s2);
    }
}

结果展示: 

优点: 线程任务类只是实现接口可以继续继承类和实现接口, 扩展性强

可以在线程完毕后去获取线程执行的结果

缺点: 代码有点复杂

二.Thread的常用方法 

1.Thread的构造方法

            方法名称                                                                          说明
public Thread(String name)                                      可以为当前线程指定名称
public Thread(Runnable target)                         封装Runnable对象成为线程对象
public Thread(Runnable target ,String name )  封装Runnable对象成为线程对象,并指定线程名称

2.Thread获取和设置线程名称

           方法名称                                                                    说明
String getName()                          获取当前线程的名称,默认线程名称是Thread-索引
void setName(String name)     将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称

3.Thread类获得当前线程的对象

                        方法名称                                                                说明
public static Thread currentThread()              返回对当前正在执行的线程对象的引用
代码示范: 第一段类的代码, 第二段是测试的代码

public class MyThread extends Thread{
    public MyThread() {
    }
    //调用父类的方法
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(currentThread().getName()+"输出: "+i);
        }
    }
}
import static java.lang.Thread.currentThread;

public class ThreadAPI {
    public static void main(String[] args) {

        Thread t1 = new MyThread("一号子线程");
        //t1.setName("一号");//设置线程名称
        t1.start();

        Thread t2 = new MyThread("二号子线程");
        t2.start();
        //输出子线程1的名称
        System.out.println(t1.getName());
        //输出子线程2的名称
        System.out.println(t2.getName());
        //输出主线程的名称
        System.out.println(currentThread().getName());
        //设置主线程名称
        currentThread().setName("主线程");
        for (int i = 0; i < 5; i++) {
            System.out.println(currentThread().getName()+"输出: "+ i);
        }


    }
}

结果展示: 

扩展:Thread类的线程休眠方法
                             方法名称                                                      说明
public static void sleep(long time) 让当前线程休眠指定的时间后再继续执行,单位为毫秒
代码示范: 

mport static java.lang.Thread.currentThread;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            if(i==3){
                Thread.sleep(5000);
            }
            System.out.println(currentThread().getName()+"输出: "+i);
        }
    }
}

三.线程安全

线程安全: 多个线程同时操作一个共享资源的时候可能出现业务安全问题

原因:多个线程同时访问同一个共享资源且存在修改该资源

如: 两个人在同一个账户取钱

代码示范: 

public class Account {
    private String Id;//卡号
    private double money;//账户余额

    public Account() {
    }

    public Account(String Id, double money) {
        this.Id = Id;
        this.money = money;
    }

    /**
     * 获取
     *
     * @return Id
     */
    public String getId() {
        return Id;
    }

    /**
     * 设置
     *
     * @param Id
     */
    public void setId(String Id) {
        this.Id = Id;
    }

    /**
     * 获取
     *
     * @return money
     */
    public double getMoney() {
        return money;
    }

    /**
     * 设置
     *
     * @param money
     */
    public void setMoney(double money) {
        this.money = money;
    }

    public String toString() {
        return "Account{Id = " + Id + ", money = " + money + "}";
    }

    public void drawMoney(double money) {
        //1.谁来取钱
        String name = Thread.currentThread().getName();
        //2.判断钱是否足够
        if (this.money >= money) {
            //3.取钱
            System.out.println(name + "来取钱成功,吐出: " + money);
            //4.更新余额
            this.money -= money;
            System.out.println(name+"来取钱后,剩余: "+this.money);
        }else{
            System.out.println(name+"来取钱失败,余额不足");
        }
    }
}
public class DrawThread extends Thread{
    //接收处理的账户对象
    private Account acc;

    public DrawThread(Account acc,String name) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        //取钱
        acc.drawMoney(100000);
    }
}
public class Test1 {
    public static void main(String[] args) {
        //创建一个共享账户
        Account acc = new Account("abc-123",100000);
        //创建两个线程,代表两个人来取钱
        new DrawThread(acc,"小明").start();
        new DrawThread(acc,"小红").start();
    }
}

结果展示: 

四.线程同步

加锁: 让多个线程实现先后依次访问共享资源

1.同步代码块

(1)作用: 把出现线程安全问题的核心代码上锁

(2)原理: 每次只能一个线程进入, 执行完毕后自动解锁, 其他线程才可以进来执行

(3)格式: synchronized(同步锁对象){

        操作共享资源的代码(核心代码)

}

(4)锁对象的规范要求:

建议使用共享资源作为锁对象

对于实例方法建议使用this作为锁对象

对于静态方法建议使用字节码 (类名.class)对象作为锁对象

(格式:synchronized (类名.class){ })

以上一个取钱的线程安全问题为例

 修改后的结果展示: 

2.同步方法

作用: 把出现线程安全问题的核心方法给上锁

原理: 每次只能一个线程进入, 执行完毕之后自动解锁, 其他线程才可以进来执行

格式: 修饰符 synchronized 返回类型 方法名称(形参列表){

        操作共享资源的代码

}

以上一个取钱的线程安全问题为例 修改后的结果展示:

3.Lock锁

为了更清晰的表达如何加锁和释放锁, JDK5以后提供了一个新的锁对象Lock, 更加灵活,方便Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象
             方法名称                                       说明
public ReentrantLock()            获得Lock锁的实现类对象
Lock的API
方法名称                         说明
void lock()                    获得锁
void unlock()                释放锁

代码示范: 使用Lock锁对Account类进行修改

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
    private String Id;//卡号
    private double money;//账户余额
    private final Lock lock = new ReentrantLock();//用final修饰,锁对象是唯一且不可替换的

    public Account() {
    }
    public Account(String Id, double money) {
        this.Id = Id;
        this.money = money;
    }
    public void drawMoney(double money) {
        //1.谁来取钱
        String name = Thread.currentThread().getName();
        lock.lock();//上锁
        //2.判断钱是否足够(核心代码)
        try {
            if (this.money >= money) {
                //3.取钱
                System.out.println(name + "来取钱成功,吐出: " + money);
                //4.更新余额
                this.money -= money;
                System.out.println(name + "来取钱后,剩余: " + this.money);
            } else {
                System.out.println(name + "来取钱失败,余额不足");
            }
        } finally {
            lock.unlock();//解锁
        }
    }

    /**
     * 获取
     *
     * @return Id
     */
    public String getId() {
        return Id;
    }

    /**
     * 设置
     *
     * @param Id
     */
    public void setId(String Id) {
        this.Id = Id;
    }

    /**
     * 获取
     *
     * @return money
     */
    public double getMoney() {
        return money;
    }

    /**
     * 设置
     *
     * @param money
     */
    public void setMoney(double money) {
        this.money = money;
    }

    public String toString() {
        return "Account{Id = " + Id + ", money = " + money + "}";
    }


}

结果展示: 

五.线程通信

什么是线程通信、如何实现?

所谓线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现
线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做

线程通信常见模型

生产者与消费者模型:生产者线程负责生产数据,消费者线程负责消费数据(一产一消)

要求:生产者线程生产完数据后,唤醒消费者,然后等待自己(把自己关闭, 把cpu占的空间腾出来,提高运行效率);消费者消费完该数据后,唤醒生产者,然后等待自己
 

六.线程池

1.线程池就是一个可以复用线程的技术

如果不使用线程池的问题会产生什么问题?
如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能

JDK 5.0起提供了代表线程池的接口: ExecutorService

如何得到线程池对象?

1.使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

2.使用Executors (线程池的工具类) 调用方法返回不同特点的线程池对象

ThreadPoolExecutor构造方法的参数说明
public ThreadPoolExecutor(int corePoolSize,

                                                int maximumPoolSize,

                                                long keepAliveTime,

                                                TimeUnit unit,

                                                BlockingOueue<Runnable> workQueue ,

                                                ThreadFactory threadFactory,
                                                RejectedExecutionHandler handler)
参数一:指定线程池的线程数量(核心线程): corePoolSize------->不能小于0
参数二:指定线程池可支持的最大线程数: maximumPoolsize------>最大数量>=核心线程数量
参数三:指定临时线程最大存活时间: keepAliveTime-------->不能小于0
参数四:指定存活时间的单位(秒、分、时、天): unit--------->时间单位
参数五:指定任务队列(在等待执行的任务): workQueue---------->不能为null
参数六:指定用哪个线程工厂创建线程: threadFactory---------->不能为null
参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler--------->不能为null

2.线程池常见面试题

临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
什么时候会开始拒绝任务?
核心线程临时线程都在忙任务队列也满了新的任务过来的时候才会开始任务拒绝

3.ExecutorService的常用方法

       方法名称                                                                           说明
void execute(Runnable command)   执行任务/命令,没有返回值,一般用来执Runnable任务
Future<T> submit(Callable<T> task)   执行任务,返回未来任务对象获取线程结果,一般拿来执行 callable 任务
void shutdown()                                                  等任务执行完毕后关闭线程池
List<Runnable> shutdownNow()       立刻关闭,停止正在执行的任务,并返回队列中未执行的任务

代码示范: 处理Runnable任务

public class MyRunnable extends Thread{
    @Override
    public void run() {
        for (int i = 1; i < 4; i++) {
            System.out.println(Thread.currentThread().getName()+"输出: "+i);
        }
        System.out.println(Thread.currentThread().getName()+"完成一次");
    }
}
import java.util.concurrent.*;

public class Test1 {
    public static void main(String[] args) {
        /*public ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,
                                    ThreadFactory threadFactory,
                                    RejectedExecutionHandler handler)*/
        ExecutorService pool = new ThreadPoolExecutor(3,6,4
                ,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());
        Runnable mr = new MyRunnable();
        pool.execute(mr);
        pool.execute(mr);
        pool.execute(mr);
        pool.execute(mr);
        pool.execute(mr);
        pool.execute(mr);
    }
}

结果展示: 

 代码示范: 处理Callable任务

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName()+"输出: "+sum;
    }
}
import java.util.concurrent.*;

public class Test2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(3,6,4
                , TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));
        Future<String> f5 = pool.submit(new MyCallable(500));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
        System.out.println(f5.get());
    }
}

结果展示: 

4.Executors得到线程池对象的常用方法

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
(1)public static ExecutorService newCachedThreadPool()                                                  线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉
(2)public static ExecutorService newFixedThreadPool(int nThreads)            创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它
(3)public static ExecutorService newSingleThreadExecutor ()                        创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程
(4)public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)    创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的

代码示范: 

public class MyRunnable extends Thread{
    @Override
    public void run() {
        for (int i = 1; i < 4; i++) {
            System.out.println(Thread.currentThread().getName()+"输出: "+i);
        }
        System.out.println(Thread.currentThread().getName()+"完成完成本轮输出");
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test3 {
    public static void main(String[] args) {
        //创建固定线程数量的线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());

    }
}

结果展示: 

缺点: Executors不适合大型互联网场景的线程池方案
建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则规避资源耗尽的风险

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8 标志寄存器

标志寄存器 CPU 内部的寄存器中&#xff0c;有一种特殊的寄存器&#xff08;对于不同的处理机&#xff0c;个数和结构都可能不同&#xff09;具有以下3 种作用。 用来存储相关指令的某些执行结果&#xff1b;用来为CPU 执行相关指令提供行为依据&#xff1a;用来控制CPU 的相…

Pyinstaller打包exe程序

Pyinstaller和Nuitka是两大热门的python打包路径&#xff0c;学习Nuitka可以点击Nuitka入门学习。本文主要介绍Pyinstaller打包过程。 1.创建python虚拟环境 在conda中创建一个虚拟环境&#xff0c;用于程序打包&#xff0c;可以使打包程序占用空间最小 使用一下指令&#x…

一个超方便将现有博客生成vuepress2文档站的插件

闲来无事&#xff0c;研究了一下vuepress2和它的插件机制&#xff0c;写了一个可以一键通过已有博客生成vuepress2的文档站的vuepress2插件vuepress-plugin-blog-sync 效果 在vuepress2中简单引入即可达到将政采云掘金博客一键生成vuepress2页面&#xff0c;效果✨ 详见Demo …

38_SPI通信原理

SPI接口简介 SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。 SPI,是一种高递的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,…

蔚来智驾功能大更新:与其叫NOP+,不如叫NAD-

HiEV消息&#xff08;文/张祥威&#xff09;赶在2022年最后一个月&#xff0c;蔚来的自动驾驶大招露出了冰山一角。 2020年10月&#xff0c;蔚来国内首家推送高速场景下的领航辅助NOP&#xff08;Navigate on Pilot&#xff09;&#xff0c;当时只有特斯拉实现了类似功能&#…

English Learning - L1 站在高处建立灵魂 2022.12.5 周一

English Learning - L1 站在高处建立灵魂 2022.12.5 周一1.1 到底什么是语法1.2 为什么要学习语法口语分广义和狭义讲母语的人为啥不学语法&#xff1f;作为一名二语习得者口语中可不可以没有有语法&#xff1f;1.3 英语&#xff08;听说读写&#xff09;的核心金字塔理论关于词…

[附源码]计算机毕业设计基于web的羽毛球管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

数据结构作业——第十四周——AOE网+查找

1. 单选题 简单 5分 若一个有向图中的顶点不能排成一个拓扑序列&#xff0c;则可断定该有向图______。 A.是个有根有向图 B.是个强连通图 C.含有多个入度为0的顶点 D.含有顶点数目大于1的强连通分量 回答正确 解析 2 . 单选题 简单 5分 以下关于图拓扑排序的叙述中正确的…

自动驾驶之去光晕调研

中文版综述github 一、光晕类型 常见去光晕算法的光晕 去光晕算法的光晕之二(汇总) 样式包括有: halos(色圈), streaks(条纹), bright lines(亮线), saturated blobs(深污点), color bleeding(色渗), haze(烟雾), and many others。 二、光晕成因 一个理想的相机&#x…

JSP SSH校园兼职信息发布平台myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 JSP SSH校园兼职信息发布平台是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采 用B/S模式开发。开发环境为TOMCA…

Docker Images Explore

Docker Images Explore scratch an explicitly empty image, especially for building images “FROM scratch” You can use Docker’s reserved, minimal image, scratch, as a starting point for building containers. Using the scratch “image” signals to the build …

自定义键盘快捷键调节电脑音量

外接的键盘没有Fn。。。也没有调音量的键&#x1f605;于是想自己弄一个 方法一&#xff1a;修改注册表键盘映射 新建记事本文件打开&#xff0c;粘贴如下内容&#xff1a; Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Key…

Idea 调试自定义 AbstractProcessor 程序

我们常使用的 Lombok 可以自动生成 gettr 和 setter 方法,使用起来非常方便;有时候我们也要去实现自己的注解处理器,实现一些在编译阶段需要执行的逻辑,例如我之前写的 spring cloud 自动生成 openfeign 的Fallback 降级方法;自动生成 openfeign 的Fallback 但是自定义注…

JavaEE进阶:SpringBoot概念、创建和运⾏

文章目录一、Spring Boot 优点二、Spring Boot 项⽬创建1、使用 Idea 创建① 准备工作② 创建项目③ 注意事项2、网页版创建&#xff08;了解&#xff09;三、项目目录介绍和运行1、运行项目2、输出 Hello world四、注意事项&#xff1a;包路径错误1、正确路径2、小结&#xff…

第十四届蓝桥杯集训——JavaC组第九篇——三元运算符

第十四届蓝桥杯集训——JavaC组第九篇——三元运算符 一元运算符(一元运算符有1个操作数) &#xff0c;- -都是运算符&#xff0c;- -可分为前&#xff0c;后&#xff0b;和前-&#xff0c;后减如果在后面&#xff0c;如&#xff1a;num 10&#xff1b;先参与运算&#xff0c;然…

基于混合NSGA II-MOPSO算法的热电联合经济排放调度(Matlab代码实现)【混合多目标遗传算法-多目标粒子群算法】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f680;支持&#xff1a;&#x1f381;&#x1f381;&#x1f381;如果觉得博主的文章还不错或者您用得到的话&…

游戏开发43课 移动游戏性能优化1

1. 前言 很多年前就想将这些年工作中积累的优化经验撰写成文章&#xff0c;但懒癌缠身&#xff0c;迟迟未动手&#xff0c;近期总算潜下心写成文章。 涉及到具体优化技巧时&#xff0c;博主会尽量阐述原理和依据&#xff0c;让读者知其然也知其所以然。要完全读懂这篇文章&am…

尚医通MyBatis-Plus入门、添加、主键策略(二)

目录&#xff1a; &#xff08;1&#xff09;MyBatis-Plus入门案例 &#xff08;2&#xff09;MyBatis-Plus-添加和主键生成策略 &#xff08;1&#xff09;MyBatis-Plus入门案例 简介&#xff1a; MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&a…

c51单片机烧录程序 控制台显示正在检测目标单片机

问题 c51单片机烧录程序 控制台显示正在检测目标单片机 详细问题 笔者使用单片机型号STC89C52&#xff0c; 进行程序烧录。控制台显示正在检测目标单片机 解决方案 将单片机型号由STC89C52更改为STC89C52RC 即 原因 STC89C52与STC89C52RC属于不同型号&#xff0c;STC89C…

NoSQL数据库原理与应用综合项目——起始篇

NoSQL数据库原理与应用综合项目——起始篇 文章目录NoSQL数据库原理与应用综合项目——起始篇0、 写在前面1、项目说明1.1 项目背景1.2 项目功能2、数据集和数据预处理2.1 数据集2.2 数据预处理2.2.1 图书出版日期字段的处理2.2.2 添加id字段2.2.3 价格字段的处理2.2.4 打折字段…