Когда мы работаем с несколькими потоками, может сложиться ситуация, при которой, несколько потоков пытаются получить доступ к одному и тому же объекту, что может привести к непредсказуемым результатам.
Например, несколько потоком пытаются внести изменения в один и тот же текстовый файл. В результате этого данные могут быть повреждены, так как один поток пытается записать данные “поверх” данных второго потока.
Для предотвращения таких ситуаций применяется синхронизация.
Синхронизация – это механизм, который гарантирует, что только один поток может работать с определёнными данным в один момент времени.
Для понимания того, как то работает на практике, рассмотрим примеры двух простых приложений.
Пример без применения синхронизации:
Класс Counter
public class Counter {
public void displayCounter(){
try {
for(int i = 1; i<=5; i++){
System.out.println("Counter: " + i);
}
}catch (Exception e){
System.out.println("Thread is interrupted.");
}
}
}
Класс SimpleThread
public class SimpleThread extends Thread{
private Thread thread;
private String threadName;
Counter counter;
public SimpleThread(String threadName, Counter counter) {
this.threadName = threadName;
this.counter = counter;
}
public void run(){
System.out.println("Thread " + threadName + " is running...");
counter.displayCounter();
System.out.println("Leaving " + threadName + " thread...");
}
public void start(){
System.out.println("Thread " + threadName + " is started.");
if(thread == null){
thread = new Thread(this,threadName);
thread.start();
}
}
}
Класс SimpleThreadDemo
public class SimpleThreadDemo {
public static void main(String[] args) {
Counter counter = new Counter();
SimpleThread threadOne = new SimpleThread("Thread One", counter);
SimpleThread threadTwo = new SimpleThread("Thread Two", counter);
threadOne.start();
threadTwo.start();
try {
threadOne.join();
threadTwo.join();
}catch (InterruptedException e){
System.out.println("Threads interrupted.");
e.printStackTrace();
}
}
}
В результате работы программы мы получим, примерно, следующий результат:
/*Some System Messages*/
Thread Thread One is started.
Thread Thread Two is started.
Thread Thread Two is running...
Thread Thread One is running...
Counter: 1
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 2
Counter: 3
Counter: 5
Counter: 4
Leaving Thread Two thread...
Counter: 5
Leaving Thread One thread...
Пример с применением синхронизации:
Класс Counter
public class Counter {
public void displayCounter(){
try {
for(int i = 1; i<=5; i++){
System.out.println("Counter: " + i);
}
}catch (Exception e){
System.out.println("Thread is interrupted.");
}
}
}
Класс SynchThread
public class SynchThread extends Thread {
private Thread thread;
private String threadName;
final Counter counter;
public SynchThread(String threadName, Counter counter) {
this.threadName = threadName;
this.counter = counter;
}
public void run() {
System.out.println("Thread " + threadName + " is running...");
synchronized (counter) {
counter.displayCounter();
}
System.out.println("Leaving " + threadName + " thread...");
}
public void start() {
System.out.println("Thread " + threadName + " successfully started.");
if (thread == null) {
thread = new Thread(this, threadName);
thread.start();
}
}
}
Класс SynchThreadDemo
public class SynchThreadDemo {
public static void main(String[] args) {
Counter counter = new Counter();
SynchThread threadOne = new SynchThread("Synchronized Thread One", counter);
SynchThread threadTwo = new SynchThread("Synchronized Thread Two", counter);
threadOne.start();
threadTwo.start();
try {
threadOne.join();
threadTwo.join();
}catch (InterruptedException e){
System.out.println("Threads interrupted.");
e.printStackTrace();
}
}
}
В результате работы программы, мы получим, примерно, следующий результат:
/*Some System Messages*/
Thread Synchronized Thread One successfully started.
Thread Synchronized Thread Two successfully started.
Thread Synchronized Thread One is running...
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Leaving Synchronized Thread One thread...
Thread Synchronized Thread Two is running...
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Leaving Synchronized Thread Two thread...
Как мы можем видеть, в примере с применением синхронизации, сначала мы работаем с первым потоком, а затем – со вторым. В то время как в примере без синхронизации мы получаем доступ к потокам хаотично.