Skip to content

多线程的协调控制顺序

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))
        );
    }
}