Synchronization is the coordination of two or more
tasks to get the desired results.
Control synchronization: One task depends on the end
of another task, the second task can't start before the first has finished.
Data access synchronization: Tasks have access to a
shared variable and only one of the tasks can access the variable at any
given time.
Critical section: A piece of code that can be only executed by a task at any given time because of its
access to a shared resource.
Synchronization helps us to avoid concurrency problems but this comes at a cost. So if our algorithm will not be more efficient because of heavy synchronization, then a serial program may perform better.
We can use a semaphore to control the
access to a resource. A mutex (short for mutual exclusion) is a special
kind of semaphore that can take only two values (resource is free and resource
is busy), and only the process that sets the mutex to busy can release it.
Monitor???
Tasks can use a shared memory or use message passing in a predefined protocol.
NOTE:
Not every algorithm can be parallelized. For example, if you have to execute a loop where the result of an iteration depends on the result of the previous iteration, you can't parallelize that loop.
An Example With ThreadLocal
Sometimes we want each thread to have it's own variable. The following example shows 2 threads and variables of type MyClass:
class MyThread implements Runnable {
public boolean run;
public static final ThreadLocal a = ThreadLocal.withInitial(() -> new MyClass());
public static final ThreadLocal b = new ThreadLocal<>();
public static final MyClass c = new MyClass();
@Override
public void run() {
System.out.println("Hash code of a: " + a.get().hashCode());
System.out.println("Hash code of b: " + b.hashCode());
System.out.println("Hash code of c: " + c.hashCode());
//System.out.println("I am running!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyClass {
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.run = true;
Thread thread = new Thread(myThread);
thread.start();
MyThread myThread2 = new MyThread();
myThread2.run = true;
Thread thread2 = new Thread(myThread2);
thread2.start();
Thread.sleep(1000L);
myThread.run = false;
System.out.println("end of main method");
}
}
OUTPUT:
Hash code of a: 654647086
Hash code of a: 620785905
Hash code of b: 700642750
Hash code of b: 700642750
Hash code of c: 771585118
Hash code of c: 771585118
end of main method
So the proper way to have a different hashCode for each thread is the example of the variable a.
According to Java documentation, hashCode method gives a distinct value for each different object. Since we cannot get the address of an object in Java, we can use hashCode instead.
Comments