【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();
    }
}
相关推荐
亦暖筑序2 小时前
《Spring AI 实战系列 入门篇》第 3 篇
java
golang学习记2 小时前
Go 实时批处理:让数据库少挨点打 [特殊字符]
开发语言·数据库·golang
Memory_荒年2 小时前
Netty:从“网络搬砖”到“流水线大师”的奇幻之旅
java·后端
ChaseDreamRunner2 小时前
如何用 NSSM 把 Jar 做成 Windows 服务
java·windows·jar
神の愛2 小时前
java的Aop
java·开发语言
左左右右左右摇晃2 小时前
ConcurrentHashMap ——put + get
java·开发语言·笔记
今夕资源网2 小时前
零基础 Python 环境搭建工具 一键安装 Python 环境自动配置 升级 pip、setuptools、wheel
开发语言·python·pip·环境变量·python环境变量·python自动安装
啥咕啦呛2 小时前
java打卡学习4:HashMap底层结构、扩容机制
java·学习·哈希算法
qq_297574673 小时前
K8s系列第十四篇:K8s 故障排查实战:常见故障定位与解决方法
java·docker·kubernetes