多线程等待的最佳实践

时间:2019-08-30 06:54:33

标签: java multithreading

我正在使用spring框架编写应用程序。我有一个用户管理应用程序,可以在其中创建用户及其地址,每个用户及其地址都会收到一个使用其他类生成的ID。因为每个ID生成都需要时间,所以我想在一个线程中创建ID。我希望每个地址创建都等到它的ID准备好为止,以便用户使用。

尝试使用等待,但有时看起来效果不佳,我不希望所有地址都等到所有地址都写完之后,我宁愿每次等待都只等待自己的ID。

 public UserDto createUser(UserDto userDto) {

    UserEntity storedUserDetails = userRepository.findByEmail(userDto.getEmail());

    if (userRepository.findByEmail(userDto.getEmail()) != null) {
        throw new RuntimeException("Record already exists");
    }

    final String[] userId = new String[1];
    final CountDownLatch userIdLatch = new CountDownLatch(1);

    int addressesSize = userDto.getAddresses().size();
    final String[] addressesId = new String[addressesSize];
    final CountDownLatch[] addressesIdLatches = new CountDownLatch[addressesSize];
    for (int i = 0; i < addressesSize; i++) {
        addressesIdLatches[i] = new CountDownLatch(1);
    }


    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < addressesSize; i++) {
                addressesId[i] = utils.generateAddressId(30);
                addressesIdLatches[i].countDown();
            }
            userId[0] = utils.generateUserId(30);
            userIdLatch.countDown();
        }
    });
    thread.start();


    for (int i = 0; i < addressesSize; i++) {
        AddressDto addressDto = userDto.getAddresses().get(i);
        addressDto.setUserDetails(userDto);
        try {
            addressesIdLatches[i].await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        addressDto.setAddressId(addressesId[i]);
        userDto.getAddresses().set(i, addressDto);
    }

    ModelMapper modelMapper = new ModelMapper();

    UserEntity userEntity = modelMapper.map(userDto, UserEntity.class);

    try {
        userIdLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    userEntity.setUserId(userId[0]);
    userEntity.setEncryptedPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));


    UserEntity storedUserEntity = userRepository.save(userEntity);

    UserDto returnedValue = new UserDto();
    BeanUtils.copyProperties(storedUserEntity, returnedValue);

    return returnedValue;
}

ER->每个地址都等待,直到其ID就绪为止(无需等待其他地址)。

AR->引发异常

1 个答案:

答案 0 :(得分:0)

非常简单的例子

public class Main {

public static void main(String[] args) throws Exception {
    User user = new User();
    int count = 100;
    user.adrs = new String[count];
    while (count > 0) {
        count--;
        user.adrs[count] = "adr" + count;
    }
    int userAddressesCount = user.adrs.length;
    ExecutorService service = Executors.newFixedThreadPool(userAddressesCount);

    Map<String, Future<String>> adrMap = new HashMap<>();
    for (int i = 0; i < userAddressesCount; i++) {
        String oldAdr = user.adrs[i];
        adrMap.put(oldAdr, service.submit(new AddressReceiver(oldAdr)));
    }

    for (String key : adrMap.keySet()) {
        int idxOfAdr = user.findIdxOfAdr(key);
        //something like enrich address
        user.adrs[idxOfAdr] = adrMap.get(key).get();
        System.out.println("ASYNC: " + user.adrs[idxOfAdr]);
    }

    System.out.println("#################################");

    for(String adr: user.adrs) {
        System.out.println(adr);
    }
    service.shutdown();
}

static class User {
    String id;
    String[] adrs;

    private int findIdxOfAdr(String adr) {
        for (int i = 0; i < this.adrs.length; i++) {
            if (this.adrs[i].equals(adr)) return i;
        }
        return -1;
    }
}

static class AddressReceiver implements Callable<String> {
    private final String someValueForGeneration;

    public AddressReceiver(String someValueForGeneration) {
        this.someValueForGeneration = someValueForGeneration;
    }

    @Override
    public String call() throws Exception {
        Thread.sleep((int) (Math.random() * 10000));
        return someValueForGeneration + " enrich adr";
    }
}

}