I'm new to multithreading and don't even understand what to do with a thread in my application. The application is a console game. The player chooses a hero, clan and actions. Gold is credited to the treasury for various actions. You can add from your pocket, complete a task or win a battle. Each action is a separate class. All operations are saved to the database. Here is the code that creates the operation object, it also saves changes to the clan treasury
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);
}
The problem is that it is implied that many players will simultaneously perform different actions and change the treasury of their clan. According to the task, it is necessary to enter multithreading, but the amount of the clan treasury before and after should be displayed correctly.
I also created a thread with the launch of the game, which pulls a large chain of method calls from different classes.
public class ThreadGame extends Thread {
HeroService heroService = new HeroService();
public ThreadGame() {
this.start();
}
@Override
public void run() {
heroService.startGame();
}
}
Question 1. Can methods from the chain also be considered threads? Or are they no longer part of it?
I also tried to create tests for my application, but some operations are not saved in the database and synchronized does not help.
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();
}
}
I synchronized the createOperationForTest method, but the data is still stored incorrectly. Question 2. Where to specify synchronized?
CodePudding user response:
- All the code you write in the
run()
function, will run in the thread that will be created when you executethread.start();
For example:
In your class ThreadGame
you have this function:
@Override
public void run() {
System.out.println("Hello, I'm a thread");
heroService.startGame();
}
When you execute the .start()
function, a thread will be created and this thread will then execute the code in the run()
function.
So in this case it will output "Hello, I'm a thread" and then execute your heroService.startGame()
function.
All the code that you wrote in startGame()
will be executed on this thread.
Note that you can create another thread, inside a thread.
- Threads exist to provide asynchronous execution. If you need to let a thread wait until another thread has completed something, you can use Semaphores! Here's a link to learn more about semaphores.
CodePudding user response:
Elisaveta.
To learn about multi-threading I would recommend:
But in short a thread lets us run something in parallel and use multiple cores of our CPU.
A good example of thread usage is a web-server.
The web-server receives HTTP-requests and for each requests it replies back with an HTTP-response.
In order to use all available CPU cores the web-server uses several threads (it's usually called "thread pool").
When a new HTTP-request arrives to the web-server the main thread delegate the task of the request processing to one of the vacant threads in the thread pool.
The thread is busy until it finishes the request processing and sends the HTTP-response, but after that it again becomes vacant and can be used to process new requests.
It's a frequent pattern in Java to have a thread pool to perform tasks in parallel.
In your case you can use threads to save new operations in parallel.
Something like this:
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();
}
}
}
With multiple threads you should be careful with static variables (it seems like CurrentHero
in your code is a static variable that stores current hero).
When you process two operations in parallel there could be two current heroes.
In multi-threaded applications such information is usually passed explicitly to methods (sometimes multiple properties are grouped in a single context
object which is usually a Map<String,Object>
that stores an object-value for every property name).
synchronized
is used when we want to guarantee that some block of code can only be executed by one thread at a same time.
Often we guard with synchronized
a code that works with some shared resource (for example an established connection to the database might only be used by one thread at the same time).
Also synchronized
allows us to guarantee that some group of actions happens atomically (i.e. the actions aren't interleaved with actions in parallel threads).
For your example synchronized
might be required:
- around
clanService.changeAmount(sum)
: here we have a shared resource "treasury".
If the operationchangeAmount
consists internally of several actions, then you might want to execute them atomically. - around
operationDAO.save(operation)
: here we have a shared resource "operation storage".
Again ifsave
consists internally of multiple actions, then you might want to execute them atomically.
Additionally ifoperationDAO
use internally connection to a database, then this connection might require to be used by one thread at a time - around
If you want these two operations to execute as a single atomic block.operationDAO.save(operation); clanService.changeAmount(sum);