The synchronized
keyword on an instance method prevents concurrent calls of that method when called on the same instance.
Try the following (1):
class SynchTest {
public static void main(String[] args) {
// Note that we create a new Task each time.
new Thread(new Task()).start();
new Thread(new Task()).start();
new Thread(new Task()).start();
}
static class Task implements Runnable {
long start;
Task() {
this.start = System.currentTimeMillis();
}
@Override
public synchronized void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.println(System.currentTimeMillis() - start);
}
}
}
That will output something like:
1000
1001
1001
In other words, the time elapsed for every task since its creation was about 1 second, so this means they were allowed to all run at the same time.
Now try the following (2):
class SynchTest {
public static void main(String[] args) {
// Now we pass the same instance each time.
Task t = new Task();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
static class Task implements Runnable {
long start;
Task() {
this.start = System.currentTimeMillis();
}
@Override
public synchronized void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.println(System.currentTimeMillis() - start);
}
}
}
Passing the same instance to all 3 threads means that the threads will try to call run
on the same instance. This outputs something like the following:
1001
2001
3001
In other words, each task was made to wait for the previous one.
If you really want to synchronize
all of run
for every object, you can specify your own monitor object (3):
class SynchTest {
public static void main(String[] args) {
new Thread(new Task()).start();
new Thread(new Task()).start();
new Thread(new Task()).start();
}
static class Task implements Runnable {
long start;
Task() {
this.start = System.currentTimeMillis();
}
static final Object STATIC_MONITOR = new Object();
@Override
public void run() {
synchronized (STATIC_MONITOR) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.println(System.currentTimeMillis() - start);
}
}
}
}
That outputs the same as example 2, even though we create a new task every time.
See also: Should you synchronize the run method? Why or why not?