多线程的协调控制顺序
CountDownLatch T1 T2 T3 按照顺序执行
java
package com.me.test;
import java.util.concurrent.CountDownLatch;
/**
* T1 T2 T3 顺序执行
*
* T1 执行完毕后 调用 countDownLatch.countDown(); 通知 T2
* T2 一致处于阻塞态 countDownLatch.await(); 当T1完成后 自动执行
* 执行完毕后 调用 countDownLatch2.countDown();
*
* T3 一致处于阻塞态 countDownLatch2.await(); 当T2完成后 自动执行
*
*
*
*/
public class Test1123 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
CountDownLatch countDownLatch2 = new CountDownLatch(1);
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"hello world");
countDownLatch.countDown();
}, "thread1");
Thread thread2 = new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"hello world2");
countDownLatch2.countDown();
}, "thread2");
Thread thread3 = new Thread(()->{
try {
countDownLatch2.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"hello world3");
}, "thread3");
thread.start();
thread2.start();
thread3.start();
}
}ReentrantLock Condition 接口实现
java
package com.me.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* 多线程同步 顺序 基于 ReentrantLock Condition
* 有几个线程 就有几个Condition 再加一个标志位 来控制
* 需要使用循环控制线程的等待与唤醒
*
**/
public class TestThread {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
static Condition condition2 = lock.newCondition();
static Condition condition3 = lock.newCondition();
static int flag = 0; // 0-1 1-2 2-3
public static void main(String[] args) {
Thread t1 = new Thread(()->{
try {
lock.lock();
while (flag != 0){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"hello world");
flag = 1;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}, "t1");
Thread t2 = new Thread(()->{
try {
lock.lock();
while (flag != 1){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"hello world");
flag = 2;
condition3.signal();
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}, "t2");
Thread t3 = new Thread(()->{
try {
lock.lock();
while (flag != 2){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"hello world");
flag = 0;
condition.signal();
}catch ( Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}synchronized wait notifyAll
java
package com.me.test;
/*
* 基于 synchronized wait notifyAll
*
* */
public class TestT23 {
private int flag = 0;
public synchronized void test() throws InterruptedException {
while (flag!=0){
this.wait();
}
System.out.println("thread:"+Thread.currentThread().getName()+"count:"+flag);
flag = 1;
this.notifyAll();
}
public synchronized void test2() throws InterruptedException {
while (flag!=1){
this.wait();
}
System.out.println("thread:"+Thread.currentThread().getName()+"count:"+flag);
flag = 2;
this.notifyAll();
}
public synchronized void test3() throws InterruptedException {
while (flag!=2){
this.wait();
}
System.out.println("thread:"+Thread.currentThread().getName()+"count:"+flag);
flag = 1;
this.notifyAll();
}
public static void main(String[] args) {
TestT23 testT23 = new TestT23();
Thread t1 = new Thread(()->{
try {
testT23.test();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t1");
Thread t2 = new Thread(()->{
try {
testT23.test2();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t2");
Thread t3 = new Thread(()->{
try {
testT23.test3();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}配置加密
BeanPostProcessor 基于这个拓展点实现
java
package com.me.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class IDMSProperties {
@Value("${idms.data}")
private String data;
}java
package com.me.config;
import com.me.util.AES;
import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class IDMSConfig implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof IDMSProperties){
IDMSProperties idmsConfig = (IDMSProperties) bean;
System.out.println("原始数据:"+idmsConfig.getData());
String decrypt = null;
try {
decrypt = AES.Decrypt(idmsConfig.getData());
} catch (Exception e) {
throw new RuntimeException(e);
}
idmsConfig.setData(decrypt);
System.out.println("解密数据:"+decrypt);
}
return bean;
}
}InitializingBean 方式
java
package com.me.config;
import com.me.util.AESUtil;
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class SecuretData implements InitializingBean {
@Value("${sec.data}")
private String secData ;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("SEC_DATA front :"+secData);
secData = AESUtil.decrypt(secData);
System.out.println("SEC_DATA:"+secData);
}
}多字段分组
java
package com.me.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BillDTO {
private Long id;
private String billNo; // 票据号码
private String subBillRange; // 子票区间
private BigDecimal amount; // 票据金额
private LocalDate dueDate; // 到期日
}分组键
java
package com.me.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class BillKey {
private String billNo;
private String subBillRange;
@Override
public String toString() {
return String.format("票据号码=%s, 子票区间=%s", billNo, subBillRange);
}
}分组方法实现 三种
java
package com.me;
import com.me.dto.BillDTO;
import com.me.dto.BillKey;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) throws Exception {
// 构建测试数据
List<BillDTO> billList = createTestData();
// 方式1:按票据号码+子票区间分组(复合键使用List)
Map<List<String>, List<BillDTO>> groupByBillNoAndSubRange = billList.stream()
.collect(Collectors.groupingBy(
bill -> Arrays.asList(bill.getBillNo(), bill.getSubBillRange()),
Collectors.toList()
));
// 方式2:按票据号码+子票区间分组(复合键使用自定义对象)
Map<BillKey, List<BillDTO>> groupByCustomKey = billList.stream()
.collect(Collectors.groupingBy(
bill -> new BillKey(bill.getBillNo(), bill.getSubBillRange()),
Collectors.toList()
));
// 输出分组结果
System.out.println("=== 按票据号码+子票区间分组(List复合键) ===");
groupByBillNoAndSubRange.forEach((key, bills) -> {
System.out.printf("分组键:票据号码=%s, 子票区间=%s\n", key.get(0), key.get(1));
bills.forEach(bill -> System.out.printf(" - %s\n", bill));
});
System.out.println("\n=== 按票据号码+子票区间分组(自定义Key) ===");
groupByCustomKey.forEach((key, bills) -> {
System.out.printf("分组键:%s\n", key);
bills.forEach(bill -> System.out.printf(" - %s\n", bill));
});
}
// 构建测试数据
private static List<BillDTO> createTestData() {
return Arrays.asList(
new BillDTO(1L, "BJ2025001", "001-010", new BigDecimal("10000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(2L, "BJ2025001", "001-010", new BigDecimal("15000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(3L, "BJ2025001", "011-020", new BigDecimal("20000.00"), LocalDate.of(2026, 1, 15)),
new BillDTO(4L, "BJ2025002", "001-010", new BigDecimal("5000.00"), LocalDate.of(2025, 11, 30)),
new BillDTO(5L, "BJ2025002", "001-010", new BigDecimal("8000.00"), LocalDate.of(2025, 11, 30)),
new BillDTO(6L, "BJ2025003", "021-030", new BigDecimal("30000.00"), LocalDate.of(2026, 2, 28)),
new BillDTO(7L, "BJ2025001", "001-010", new BigDecimal("12000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(8L, "BJ2025003", "021-030", new BigDecimal("25000.00"), LocalDate.of(2026, 2, 28))
);
}
}java
package com.me;
import com.me.dto.BillDTO;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test2 {
public static void main(String[] args) {
// 创建测试数据
List<BillDTO> billList = createTestData();
System.out.println("原始数据:");
billList.forEach(System.out::println);
System.out.println("=====================================");
// 使用Stream按照票据号码和子票区间进行分组
Map<String, List<BillDTO>> groupedBills = billList.stream()
.collect(Collectors.groupingBy(bill ->
bill.getBillNo() + "_" + bill.getSubBillRange()
));
// 打印分组结果
System.out.println("分组结果:");
groupedBills.forEach((key, value) -> {
System.out.println("分组键: " + key);
value.forEach(System.out::println);
System.out.println("---");
});
// System.out.println("=====================================");
//
// // 更复杂的分组:使用自定义分组键对象
// Map<GroupKey, List<BillDTO>> groupedByKeyObject = billList.stream()
// .collect(Collectors.groupingBy(bill ->
// new GroupKey(bill.getBillNumber(), bill.getSubTicketRange())
// ));
//
// System.out.println("使用自定义分组键对象的分组结果:");
// groupedByKeyObject.forEach((key, value) -> {
// System.out.println("分组键: " + key);
// System.out.println("该组票据总金额: " +
// value.stream().mapToDouble(BillDTO::getBillAmount).sum());
// value.forEach(System.out::println);
// System.out.println("---");
// });
//
// System.out.println("=====================================");
//
// // 分组并统计每个组的汇总信息
// Map<String, Double> amountSummary = billList.stream()
// .collect(Collectors.groupingBy(
// bill -> bill.getBillNo() + "_" + bill.getSubBillRange(),
// Collectors.summingDouble(BillDTO::getBillAmount)
// ));
//
// System.out.println("每个分组的金额汇总:");
// amountSummary.forEach((key, amount) -> {
// System.out.println("分组: " + key + ", 总金额: " + amount);
// });
}
private static List<BillDTO> createTestData() {
return Arrays.asList(
new BillDTO(1L, "BJ2025001", "001-010", new BigDecimal("10000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(2L, "BJ2025001", "001-010", new BigDecimal("15000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(3L, "BJ2025001", "011-020", new BigDecimal("20000.00"), LocalDate.of(2026, 1, 15)),
new BillDTO(4L, "BJ2025002", "001-010", new BigDecimal("5000.00"), LocalDate.of(2025, 11, 30)),
new BillDTO(5L, "BJ2025002", "001-010", new BigDecimal("8000.00"), LocalDate.of(2025, 11, 30)),
new BillDTO(6L, "BJ2025003", "021-030", new BigDecimal("30000.00"), LocalDate.of(2026, 2, 28)),
new BillDTO(7L, "BJ2025001", "001-010", new BigDecimal("12000.00"), LocalDate.of(2025, 12, 31)),
new BillDTO(8L, "BJ2025003", "021-030", new BigDecimal("25000.00"), LocalDate.of(2026, 2, 28))
);
}
}