【java入门到放弃】杂记

ElasticSearch使用思路

Elasticsearch 可以对数据进行增删改查;字段(mapping)可以新增,但不能删除或修改类型;字段的值是可以修改的。

往MySQL添加完数据后,提交事物,接着往ElasticSearch里添加。

原则:搜索条件用得到、过滤条件用得到、列表页要展示。满足其中一条才往ES放。不满足的字段留在MySQL,详情页用id回查就行。

根据修改时间字段(WHERE update_time > 上次同步时间),使用定时任务,增量同步MySQL数据到ES。

定时任务

小项目

  • 直接用 @Scheduled + Redis锁

中大型项目

  • 用 XXL-JOB 或 Quartz
  • 不建议自己手写复杂调度

@Configuration

Spring 的配置类标记注解,用来定义 Bean,并让 Spring 容器管理这些 Bean。

要配合@Bean使用,@Bean→ 告诉 Spring 这个方法的返回值要被管理为 Bean

如果没有@Bean

Spring 扫描它后,会把 配置类本身注册为一个 Bean

结论:不会报错,Spring 可以正常启动,只是这个配置类本身没啥用

可以开启某些功能注解

java 复制代码
//这里没有 @Bean,但 @EnableAspectJAutoProxy 会生效
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AopConfig {}

//在启动类上加上该注解也行。

//"@EnableAspectJAutoProxy(exposeProxy = true) 类似于向 Spring 容器注册了一个特殊的 Bean(处理器),只要配置类被扫描,这个功能就会生效,写在哪个配置类上都可以生效。"

当你需要给 Bean 自定义构造方法,或者在创建 Bean 时自己控制构造参数时,就必须用 @Configuration + @Bean@Component 无法实现。

@Transactional

这是事务注解,用来控制数据库事务。

指定回滚异常

@Transactional(rollbackFor = Exception.class)

默认只回滚 RuntimeException

加(rollbackFor = Exception.class), 所有异常都回滚

传播行为

@Transactional(propagation = Propagation.REQUIRED)

REQUIRED(默认):有事务就加入,没有就创建

REQUIRES_NEW:新开一个事务

也就是事物里面又有事物时,两个事物是一起提交,一起回滚,还是互不影响。

失效

java 复制代码
//1、自调用失效

this.method(); //  事务不生效,因为 Spring 用的是 AOP 代理

//通过 AOP 代理获取当前 Bean,使事物生效
 ((MyService) AopContext.currentProxy()).methodA(); //事务生效
//要求在配置类中开启 exposeProxy = true,见上节。


//2、不是 public 方法也失效
private void method() {} // 不生效


//3、try-catch 吞异常,吞了就不回滚
try {
    ...
} catch (Exception e) {
    // 不抛出 → 不回滚
}
//解决办法
//方式1:继续抛异常(最推荐)
catch (Exception e) {
    throw e; // ✅
}
//方式2:手动标记回滚
catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}    
//方式3:抛运行时异常(常见)
catch (Exception e) {
    throw new RuntimeException(e);
}

存储过程

在 MySQL 里:存储过程(Stored Procedure)就是:把一段 SQL 逻辑写好,保存在数据库中,像"函数"一样调用。

Seata失效

1、99% @Transactional失效的情况,@GlobalTransactional 会失效

2、复杂 SQL 会导致 Seata 失效(或不安全)(JOIN UPDATE、 子查询 UPDATE、存储过程、批量复杂操作)

AQS

在 Java 里:AQS(AbstractQueuedSynchronizer)是一个用来构建锁和同步器的"基础框架"。

中文一般叫:抽象队列同步器

既是一种思想,也是一个框架,但更准确说: 是一个"基于队列的同步框架"

public abstract class AbstractQueuedSynchronizer

核心思想

用 state 表示"资源状态",0:资源可用,其他表示加锁多少次。用 FIFO 队列管理等待线程,先进先出(FIFO)。 CAS(抢资源),阻塞线程:LockSupport.park(); 唤醒线程:LockSupport.unpark(thread);

AQS 的公平性,不取决于"是否进入队列",而取决于"是否允许新线程插队"。

LockSupport

LockSupport 是线程阻塞与唤醒的"原子级工具",AQS 等高级同步器正是基于它构建的。

LockSupport.unpark(thread); // 发放许可

LockSupport.park(); // 消耗许可

LockSupport 的许可是"单次许可",不是计数许可:要么有(1),要么没有(0)。

java 复制代码
import java.util.concurrent.locks.LockSupport;

public class Demo1 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("线程开始执行");
            
            LockSupport.park(); // 阻塞在这里
            
            System.out.println("线程被唤醒");
        });

        t.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {}

        System.out.println("主线程唤醒子线程");
        LockSupport.unpark(t); // 唤醒线程
    }
}

线程轮流执行

方式1:wait / notify

一个对象用来加锁,一个boolean类型的flag,两个线程里两个循环,获取锁,然后判断flag,不是就wait,是就执行,然后修改flag,再notifyAll。

java 复制代码
class AlternatingPrint {

    private final Object lock = new Object();
    private boolean flag = true;

    public void print1() {
        for (int i = 0; i < 5; i++) {
            synchronized (lock) {
                //如果用if,唤醒后,就往下执行了,可能存在伪唤醒
                //while需要跳出循环再往下执行,所以必须用while
                while (!flag) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("线程1:" + i);

                flag = false;
                lock.notifyAll();
            }
        }
    }

    public void print2() {
        for (int i = 0; i < 5; i++) {
            synchronized (lock) {
                while (flag) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("线程2:" + i);

                flag = true;
                lock.notifyAll();
            }
        }
    }

    public static void main(String[] args) {
        AlternatingPrint demo = new AlternatingPrint();

        new Thread(demo::print1).start();
        new Thread(demo::print2).start();
    }
}

方式2:ReentrantLock + Condition

java 复制代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class AlternatingPrint {

    private ReentrantLock lock = new ReentrantLock();
    //Condition 是 ReentrantLock 提供的"等待/唤醒工具",可以创建多个等待队列,比 wait/notify 更灵活
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();

    private boolean flag = true;

    public void print1() {
        for (int i = 0; i < 5; i++) {
            lock.lock();
            try {
                while (!flag) {
                    c1.await();
                }

                System.out.println("线程1:" + i);

                flag = false;
                c2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public void print2() {
        for (int i = 0; i < 5; i++) {
            lock.lock();
            try {
                while (flag) {
                    c2.await();
                }

                System.out.println("线程2:" + i);

                flag = true;
                c1.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        AlternatingPrint demo = new AlternatingPrint();

        new Thread(demo::print1).start();
        new Thread(demo::print2).start();
    }
}

方式3:LockSupport

java 复制代码
import java.util.concurrent.locks.LockSupport;

class Demo {

    static Thread t1, t2;

    public static void main(String[] args) {

        t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程1:" + i);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });

        t2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                LockSupport.park();
                System.out.println("线程2:" + i);
                LockSupport.unpark(t1);
            }
        });

        t1.start();
        t2.start();
    }
}
相关推荐
codeejun7 小时前
每日一Go-53、Go微服务--限流与降级
开发语言·微服务·golang
xier_ran7 小时前
【C++】static 关键字与 const 关键字的作用
java·数据库·microsoft
阿里嘎多学长7 小时前
2026-04-17 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Wadli7 小时前
集群C++聊天服务器
服务器·开发语言·c++
凭君语未可7 小时前
为什么需要代理?从一个基础问题理解 JDK 静态代理
java·开发语言
luoqice7 小时前
利用flv库读取flv文件时长c程序
c语言·开发语言
NotFound4867 小时前
Go语言中的图形界面开发实战解析:从GUI到WebAssembly
开发语言·golang·wasm
Makoto_Kimur7 小时前
Agent 面试速成清单
java·agent
Rust研习社7 小时前
Rust Default 特征详解:轻松实现类型默认值
开发语言·后端·rust
jiayong237 小时前
第 25 课:给学习笔记页加上搜索、标签筛选和 URL 同步
开发语言·前端·javascript·vue.js·学习