synchronized
Act on object instance: lock the given object, and obtain the lock of the given object before entering the synchronization code.
Action on instance method: it is equivalent to locking the current instance and obtaining the lock of the current instance before entering the synchronization code.
For static methods: it is equivalent to locking the current class, and obtaining the lock of the current class before entering the synchronization code.
Use
Lock instance objects
public class AccountingSync implements Runnable { static AccountingSync instance = new AccountingSync(); static int i = 0; @Override public void run() { for (int k = 0; k < 10000; k++) { synchronized (instance) { i++; } } } @Test public void testInteger() throws InterruptedException { int count = 10; Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(instance); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); } }
Lock class methods
public class AccountingSync2 implements Runnable { static AccountingSync2 instance = new AccountingSync2(); static int i = 0; public synchronized void increase() { i++; } @Override public void run() { for (int k = 0; k < 10000; k++) { increase(); } } @Test public void testInteger() throws InterruptedException { int count = 10; Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(instance); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); } }
Error demonstration of locking class methods
public class AccountingSyncBad implements Runnable { static int i = 0; public synchronized void increase() { i++; } @Override public void run() { for (int k = 0; k < 10000; k++) { increase(); } } @Test public void testInteger() throws InterruptedException { int count = 10; Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(new AccountingSyncBad()); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); } }
Let's say that each instance in locking a class instance is compared to a door. In the above test method, each door has a lock, but 10 doors have 10 locks, and each thread enters a door. Can't guarantee that the critical area resource i can be accessed by only one thread at the same time
fix
@Test public void testIntegerFix() throws InterruptedException { int count = 10; AccountingSyncBad instance = new AccountingSyncBad(); Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(instance); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); }
Lock static class methods
public class AccountingSyncClass implements Runnable { static int i = 0; public static synchronized void increase() { i++; } @Override public void run() { for (int k = 0; k < 10000; k++) { increase(); } } @Test public void testInteger() throws InterruptedException { int count = 10; Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(new AccountingSyncClass()); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); } @Test public void testIntegerFix() throws InterruptedException { int count = 10; AccountingSyncClass instance = new AccountingSyncClass(); Thread[] ts = new Thread[count]; for (int k = 0; k < count; k++) { ts[k] = new Thread(instance); } // start for (int k = 0; k < count; k++) { ts[k].start(); } // join for (int k = 0; k < count; k++) { ts[k].join(); } System.out.println(i); } }
The testInteger method and testIntegerFix method tested above can get correct results, because adding a lock to a static class method is equivalent to the same lock used by 10 doors, ensuring that only one thread can access the critical area resource i at the same time.