我是多线程的新手,甚至不知道如何处理应用程序中的线程。该应用程序是一个控制台游戏。玩家选择英雄、部落和行动。黄金因各种行为而被记入财政部。你可以从口袋里添加,完成任务或赢得战斗。每个动作都是一个单独的类。所有操作都保存到数据库中。这是创建操作对象的代码,它还保存了对部落金库的更改
public class OperationService {
OperationDAO operationDAO = new OperationDAO();
private static ClanService clanService = new ClanService();
public void createOperation(String reason, int sum) {
Hero currentHero = CurrentHero.getCurrentHero();
Clan currentClan = CurrentHero.getClan();
LocalDateTime currentDateTime = LocalDateTime.now();
Operation operation = new Operation();
operation.setClan(currentClan);
operation.setHero(currentHero);
operation.setReason(reason);
operation.setSum(sum);
operation.setAmountBefore(currentClan.getGold());
operation.setAmountAfter(currentClan.getGold() + sum);
operation.setDateTime(currentDateTime);
operationDAO.save(operation);
clanService.changeAmount(sum);
}
问题是,这意味着许多玩家将同时执行不同的动作,并改变他们部落的金库。根据任务,需要进入多线程,但要正确显示之前和之后的族库金额。
随着游戏的推出,我还创建了一个线程,它从不同的类中调用了一大串方法。
public class ThreadGame extends Thread {
HeroService heroService = new HeroService();
public ThreadGame() {
this.start();
}
@Override
public void run() {
heroService.startGame();
}
}
问题1。链中的方法也可以被视为线程吗?还是他们不再是其中的一部分
我还尝试为我的应用程序创建测试,但有些操作没有保存在数据库中,同步也没有帮助。
public class Test extends Thread {
HeroDAO heroDAO = new HeroDAO();
OperationService operationService = new OperationService();
@Override
public void run() {
Hero hero1 = heroDAO.findById(4L);
operationService.createOperationForTest(hero1, "Победа в бою", 20);
operationService.createOperationForTest(hero1, "Победа в бою", 20);
}
}
public class App {
public static void main(String[] args) {
Test test = new Test();
Test test1 = new Test();
test.start();
test1.start();
}
}
我同步了createOperationForTest方法,但数据仍然存储不正确。问题2。在哪里指定synchronized
- 在
run()
函数中编写的所有代码都将在执行thread.start();
时创建的线程中运行
例如:在你的课程ThreadGame
中,你有这样的功能:
@Override
public void run() {
System.out.println("Hello, I'm a thread");
heroService.startGame();
}
执行.start()
函数时,将创建一个线程,然后该线程将执行run()
函数中的代码。因此,在这种情况下;你好,我是线程"然后执行您的CCD_ 6函数。您在startGame()
中编写的所有代码都将在此线程上执行。请注意,您可以在一个线程内创建另一个线程。
- 存在线程以提供异步执行。如果你需要让一个线程等待另一个线程完成某件事,你可以使用Semaphores!这里有一个链接来了解更多关于信号量的信息
Elisaveta。
要了解多线程,我建议:
- 关于并发的Java官方教程
- Java并发在实践中的应用
但简而言之,线程可以让我们并行运行一些东西,并使用CPU的多个核心。
线程使用的一个很好的例子是web服务器
web服务器接收HTTP请求,并对每个请求返回HTTP响应
为了使用所有可用的CPU核心,web服务器使用几个线程(通常称为"线程池")
当新的HTTP请求到达web服务器时,主线程将请求处理任务委托给线程池中的一个空闲线程
线程在完成请求处理并发送HTTP响应之前一直处于繁忙状态,但在此之后,它再次变为空闲状态,可用于处理新的请求。
在Java中,使用线程池并行执行任务是一种常见的模式。
在您的情况下,可以使用线程并行保存新操作
类似这样的东西:
public class App {
final HeroDAO heroDAO = new HeroDAO();
final OperationService operationService = new OperationService();
final ExecutorService threadPool;
App() {
var numCores = Runtime.getRuntime().availableProcessors();
threadPool = Executors.newFixedThreadPool(numCores);
}
void saveNewOperation(long heroId, String reason, int sum) {
threadPool.submit(() -> {
var hero = heroDAO.findById(heroId);
operationService.createOperationForTest(hero, reason, sum);
});
}
void stop() throws InterruptedException {
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.SECONDS);
threadPool.shutdownNow();
}
public static void main(String[] args) throws InterruptedException {
var app = new App();
try {
app.saveNewOperation(4L, "Победа в бою", 20);
app.saveNewOperation(5L, "Победа в бою", 30);
app.saveNewOperation(6L, "Победа в бою", 40);
app.saveNewOperation(7L, "Победа в бою", 50);
} finally {
app.stop();
}
}
}
对于多线程,您应该小心使用静态变量(代码中的CurrentHero
似乎是存储当前英雄的静态变量)
当您并行处理两个操作时,可能会有两个当前英雄
在多线程应用程序中,此类信息通常显式传递给方法(有时将多个属性分组在一个context
对象中,该对象通常是为每个属性名称存储对象值的Map<String,Object>
)。
当我们希望保证某个代码块同时只能由一个线程执行时,就会使用synchronized
我们经常使用synchronized
来保护与某些共享资源一起工作的代码(例如,与数据库的已建立连接可能只能由一个线程同时使用)
此外,synchronized
允许我们保证某组操作以原子方式发生(即,这些操作不会与并行线程中的操作交错)。
对于您的示例,可能需要synchronized
:
- 围绕CCD_ 15:这里我们有一个共享资源"财政部">
如果操作changeAmount
在内部由多个操作组成,那么您可能希望以原子方式执行这些操作 - 关于CCD_ 17:这里我们有一个共享资源";操作存储器">
同样,如果save
在内部由多个操作组成,那么您可能希望以原子方式执行它们
此外,如果operationDAO
使用到数据库的内部连接,则此连接可能需要一次由一个线程使用 - 周围
如果您希望这两个操作作为单个原子块执行operationDAO.save(operation); clanService.changeAmount(sum);