We need to control access to shared state ( when multiple threads are at work ), to avoid unexpected results.
In the case of the program below the shared state is the int sheepCount.
10 different threads call the method incrementAndReport() .
Without thread management
public class Ch18SheepManager { private int sheepCount = 0; private void incrementAndReport() { Thread t = Thread.currentThread(); System.out.print("sheepcount = " + (++sheepCount) + " Worker - " + t.getName()); } public static void main(String[] args) throws Exception { var service = Executors.newFixedThreadPool(20); var manager = new Ch18SheepManager(); for (int i = 0; i < 10; i++) service.submit(() -> manager.incrementAndReport()); } }
Output ( wonky )
Every thread is trying to get access to the method incrementAndReport() , and its all over the place
sheepcount = 5 Worker - pool-1-thread-5sheepcount = 2 Worker - pool-1-thread-3sheepcount = 4 Worker - pool-1-thread-2sheepcount = 1 Worker - pool-1-thread-4sheepcount = 3 Worker - pool-1-thread-1 sheepcount = 7 Worker - pool-1-thread-7sheepcount = 6 Worker - pool-1-thread-6 sheepcount = 8 Worker - pool-1-thread-8 sheepcount = 9 Worker - pool-1-thread-9 sheepcount = 10 Worker - pool-1-thread-10
With Thread access management
Each time the method is called by a Thread, its completed by that Thread (other threads locked out until finished)
sheepcount = 1 Worker - pool-1-thread-1 sheepcount = 2 Worker - pool-1-thread-5 sheepcount = 3 Worker - pool-1-thread-6 sheepcount = 4 Worker - pool-1-thread-4 sheepcount = 5 Worker - pool-1-thread-3 sheepcount = 6 Worker - pool-1-thread-2 sheepcount = 7 Worker - pool-1-thread-8 sheepcount = 8 Worker - pool-1-thread-7 sheepcount = 9 Worker - pool-1-thread-9 sheepcount = 10 Worker - pool-1-thread-10
This is done with the help of the keyword synchronized
public class Ch18SheepManager { private int sheepCount = 0; Object lock = new Object(); /* created once here, so every thread gets SAME obj */ // synchronized /* we could do this */ private void incrementAndReport() { // Object lock = new Object(); DONT DO THIS // synchronized(this) { /* we could do this */ synchronized (lock) { Thread t = Thread.currentThread(); System.out.print("sheepcount = " + (++sheepCount) + " Worker - " + t.getName()); } } public static void main(String[] args) throws Exception { var service = Executors.newFixedThreadPool(20); var manager = new Ch18SheepManager(); for (int i = 0; i < 10; i++) service.submit(() -> manager.incrementAndReport()); } }
important ( see DONT DO THIS )
Don’t use a local method variable to synchronize on :
as it will be a new variable for every thread and the lock won’t work
Some notes for Oracle cert (OCPJP 11 ), I’ve currently studying for (last time I did a Java cert Sun Microsystems was the vendor yikes !).
This is based on a program in the Jeanne Boyarsky / Scott Selikoff book
These threads always troubles me, thanks for the help Louie. It cleared a lot of my doubts.
Thanks Jake, I’m glad it helped.
Threads are a complex subject, they confuse me too at times !