JAVA采用多种方式实现多线程编程(Synchronized、Reentrantlock、Redis的分布式锁、Actor模型)

        

目录

使用Synchronized关键字

使用ReentrantLock

使用Redis分布式锁

使用Actor模型


        假设场景:当涉及到多线程环境下的账户转账操作时,需要确保数据一致性和并发安全。以下是使用不同方式实现多线程下账户转账的示例代码:

使用Synchronized关键字

  • 转账金额的合法性检查,并在转账方法中处理了可能出现的异常。
  • 在锁的获取时,根据账户id的大小关系,避免了死锁情况的发生。
  • 在执行转账操作时,通过调用performTransfer方法完成实际的转账过程。该方法会捕获转账过程中可能出现的异常,并进行相应的处理和回滚操作,以确保数据一致性。
public class Account {
    private int balance;
    private final int id;

    public Account(int id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public synchronized void withdraw(int amount) {
        if (amount <= 0 || amount > balance) {
            throw new IllegalArgumentException("Invalid withdrawal amount");
        }
        balance -= amount;
    }

    public synchronized void deposit(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Invalid deposit amount");
        }
        balance += amount;
    }

    public int getId() {
        return id;
    }

    public int getBalance() {
        return balance;
    }
}

public class TransferManager {
    public static void transfer(Account from, Account to, int amount) {
        if (from.getId() < to.getId()) {
            synchronized (from) {
                synchronized (to) {
                    performTransfer(from, to, amount);
                }
            }
        } else if (from.getId() > to.getId()) {
            synchronized (to) {
                synchronized (from) {
                    performTransfer(from, to, amount);
                }
            }
        } else {
            throw new IllegalArgumentException("Cannot transfer to the same account");
        }
    }

    private static void performTransfer(Account from, Account to, int amount) {
        try {
            from.withdraw(amount);
            to.deposit(amount);
        } catch (Exception e) {
            // Handle exception and rollback if necessary
            // 可根据具体情况,捕获并处理转账过程中可能出现的异常,例如余额不足等
            // 如果发生异常,可以进行回滚操作,确保数据一致性
        }
    }
}

使用ReentrantLock

  • 对于转账操作,使用了tryLock()方法来尝试获取锁,避免在获取锁时发生长时间阻塞。如果获取锁失败,等待一段时间再尝试获取锁。如果连续多次尝试都未能获取锁,放弃转账操作。这样可以避免死锁和长时间阻塞,并提高性能。
  • 需要注意的是,tryLock()方法可能会返回false,因此需要在获取锁失败时进行相应的处理逻辑,例如等待一段时间再尝试获取锁或者放弃转账操作。另外,在等待一段时间后尝试获取锁时,也可能会出现竞争情况,因此需要根据具体情况进行调整等待时间和尝试次数。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
    private int balance;
    private final int id;
    private Lock lock = new ReentrantLock();

    public Account(int id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void withdraw(int amount) {
        lock.lock();
        try {
            balance -= amount;
        } finally {
            lock.unlock();
        }
    }

    public void deposit(int amount) {
        lock.lock();
        try {
            balance += amount;
        } finally {
            lock.unlock();
        }
    }

    public int getId() {
        return id;
    }

    public int getBalance() {
        return balance;
    }
}

public class TransferManager {
    public static void transfer(Account from, Account to, int amount) throws InterruptedException {
        boolean success = false;
        while (!success) {
            if (from.lock.tryLock()) {
                try {
                    if (to.lock.tryLock()) {
                        try {
                            from.withdraw(amount);
                            to.deposit(amount);
                            success = true;
                        } finally {
                            to.lock.unlock();
                        }
                    }
                } finally {
                    from.lock.unlock();
                }
            }
            // 等待一段时间再尝试获取锁
            Thread.sleep(10);
        }
    }
}

使用Redis分布式锁

  • TransferManager类的transfer方法中,添加了对获取锁成功与否的判断。如果成功获取锁,则进行转账操作;如果获取锁失败,则可以自行选择如抛出异常或者记录日志等操作。这样可以更好地处理在分布式环境下的锁获取情况。
  • 利用RedisTemplate完成对Redis分布式锁的操作。
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

public class RedisDistributedLock {
    private final RedisTemplate redisTemplate;

    public RedisDistributedLock(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public boolean acquireLock(String lockKey) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "", 1, TimeUnit.SECONDS);
        return result != null && result;
    }

    public void releaseLock(String lockKey) {
        RedisScript script = new DefaultRedisScript<>("if redis.call('get', KEYS[1]) then return redis.call('del', KEYS[1]) else return 0 end", Long.class);
        redisTemplate.execute(script, Collections.singletonList(lockKey));
    }
}

public class TransferManager {
    private RedisDistributedLock distributedLock;

    public TransferManager(RedisDistributedLock distributedLock) {
        this.distributedLock = distributedLock;
    }

    public void transfer(Account from, Account to, int amount) {
        String lockKey = "transfer_lock_" + from.getId() + "_" + to.getId();
        if (distributedLock.acquireLock(lockKey)) {
            try {
                from.withdraw(amount);
                to.deposit(amount);
            } finally {
                distributedLock.releaseLock(lockKey);
            }
        } else {
            // 获取锁失败,处理逻辑
            // 可以选择抛出异常或者记录日志等操作
        }
    }
}

使用Actor模型

  • 在该实现中,TransferActor继承了线程类,传入TransferMessage消息对象,然后进行加锁、转账操作等逻辑。TransferManager则通过线程池来执行转账操作。

        虽然以上代码实现了多线程下的转账操作,但是如果有大量的并发请求,还是会存在性能问题和死锁问题。因此,建议使用Actor框架,如Akka,来更好地处理这些问题。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Account {
    private int balance;
    private final int id;

    public Account(int id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void withdraw(int amount) {
        balance -= amount;
    }

    public void deposit(int amount) {
        balance += amount;
    }

    public int getId() {
        return id;
    }

    public int getBalance() {
        return balance;
    }
}

public class TransferMessage {
    private final Account from;
    private final Account to;
    private final int amount;

    public TransferMessage(Account from, Account to, int amount) {
        this.from = from;
        this.to = to;
        this.amount = amount;
    }

    public Account getFrom() {
        return from;
    }

    public Account getTo() {
        return to;
    }

    public int getAmount() {
        return amount;
    }
}

public class TransferActor extends Thread {
    private final TransferMessage message;

    public TransferActor(TransferMessage message) {
        this.message = message;
    }

    @Override
    public void run() {
        synchronized (message.getFrom()) {
            synchronized (message.getTo()) {
                if (message.getFrom().getBalance() >= message.getAmount()) {
                    message.getFrom().withdraw(message.getAmount());
                    message.getTo().deposit(message.getAmount());
                } else {
                    // 处理余额不足的逻辑
                    System.out.println("Insufficient balance.");
                }
            }
        }
    }
}

public class TransferManager {
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void transfer(Account from, Account to, int amount) {
        TransferMessage message = new TransferMessage(from, to, amount);
        executorService.submit(new TransferActor(message));
    }

    // 其他代码
}

优化以上代码:

  1. Account类添加了一个私有锁对象lock来避免并发访问问题。
  2. TransferActor实现了Runnable接口而不是直接继承Thread类,以更好地支持线程池执行。
  3. TransferManager调用execute方法代替submit方法,因为我们不需要获取任务的返回结果。

这些改进可以提高代码的并发性能和可靠性。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Account {
    private int balance;
    private final int id;
    private final Object lock = new Object();

    public Account(int id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void withdraw(int amount) {
        synchronized (lock) {
            balance -= amount;
        }
    }

    public void deposit(int amount) {
        synchronized (lock) {
            balance += amount;
        }
    }

    public int getId() {
        return id;
    }

    public int getBalance() {
        return balance;
    }
}

public class TransferMessage {
    private final Account from;
    private final Account to;
    private final int amount;

    public TransferMessage(Account from, Account to, int amount) {
        this.from = from;
        this.to = to;
        this.amount = amount;
    }

    public Account getFrom() {
        return from;
    }

    public Account getTo() {
        return to;
    }

    public int getAmount() {
        return amount;
    }
}

public class TransferActor implements Runnable {
    private final TransferMessage message;

    public TransferActor(TransferMessage message) {
        this.message = message;
    }

    @Override
    public void run() {
        synchronized (message.getFrom()) {
            synchronized (message.getTo()) {
                if (message.getFrom().getBalance() >= message.getAmount()) {
                    message.getFrom().withdraw(message.getAmount());
                    message.getTo().deposit(message.getAmount());
                } else {
                    // 处理余额不足的逻辑
                    System.out.println("Insufficient balance.");
                }
            }
        }
    }
}

public class TransferManager {
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void transfer(Account from, Account to, int amount) {
        TransferMessage message = new TransferMessage(from, to, amount);
        executorService.execute(new TransferActor(message));
    }

    // 其他代码
}

利用Akka框架实现此功能:

  • AccountActorcreateReceive方法中,添加了对余额不足的处理逻辑。当进行取款操作时,先判断余额是否足够,如果足够则进行取款操作,否则输出"Insufficient balance."。这样可以更好地处理余额不足的情况。
  • TransferManagertransfer方法中,修改了消息的发送者为自身(getSelf()),以便在AccountActor中可以区分消息来源。
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;

public class AccountActor extends AbstractActor {
    private int balance;

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(WithdrawMessage.class, msg -> {
                    if (balance >= msg.getAmount()) {
                        balance -= msg.getAmount();
                    } else {
                        // 处理余额不足的逻辑
                        System.out.println("Insufficient balance.");
                    }
                })
                .match(DepositMessage.class, msg -> {
                    balance += msg.getAmount();
                })
                .build();
    }
}

public class TransferManager {
    private final ActorSystem system = ActorSystem.create("transferSystem");
    private final ActorRef accountActor1 = system.actorOf(Props.create(AccountActor.class), "account1");
    private final ActorRef accountActor2 = system.actorOf(Props.create(AccountActor.class), "account2");

    public void transfer(int amount) {
        accountActor1.tell(new WithdrawMessage(amount), getSelf());
        accountActor2.tell(new DepositMessage(amount), getSelf());
    }

    // 其他代码
}

你可能感兴趣的:(java,java,redis,分布式)