Hi
Every developer will sooner or later confront with a problem that how a common object can be processed consistently in a multiple threading environment. Or in short: How to arrange multiple threads to work with one or more common object/objects without messing the results. You may say "Oh! That's easy and simple: "I use Synchronization!"
Well, true...but a bit too simple. In many cases the synchronized method or block works, but in complicated environment such as updating in a synchronized method that invokes other methods which could belong to other objects and that could be very tricky.
Huh? Where is the trickiness? The cardinal problem between synchronized block and locked block is that synchronized block or method could cause "starvation" to some other threads. Meaning: if a thread blocks an object with "synchronized" and is somehow run into troubles during the processing phase other threads could wait to death because there is no way to ask a synchronized block or method whether it is in progress or hangs...and your app could "hang" forever with it.
Java offers developers various locking mechanisms which are in the package java.util.concurrent.locks (click HERE for details) and with it developers could get rid of the dangerous "App-Hanging". The second thing is the ExcecutorService pool. Thread and Runnable Implementation are un-reusable and usually greedy for resources. That could cause problems with memory shortage or performant sluggishness. To overcome such problems the basic package java.util.concurrent (click HERE) gives the developers the tools to reuse some tasks (Callable or Runnable) and to share some resources. And that effects tremendously the processing performance.
Let start with the synchronized block or method. If the entire method that contains the objects is synchronized it is the synchronized method. If only the object is synchronized it's the synchronized block. A block is usually an object or a group of several statements. If an object is a primitive (such as int, short, double, etc.) the "this" must be used instead of the variable name.
The question is: There are several types of ExecutorService pools and what pool I should use? The answer is that it depends on the tasks you are working with. Short-live threads (Callable or Runnable) are the best with newCachedThreadPool, relative long-live threads are with newFixedThreadPool, etc. However, computer is nowadays equipped by multiple cores processor and you can exploit it with newWorkStealingPool (parallel processing). The other pools are specific (e.g. scheduling, etc.). The following screenshot shows you the difference between FixedThreadPool and WorkStealingPool


See the differences? And that is the way how the "Competitive Programming" works: same task, different outcomes.
Every developer will sooner or later confront with a problem that how a common object can be processed consistently in a multiple threading environment. Or in short: How to arrange multiple threads to work with one or more common object/objects without messing the results. You may say "Oh! That's easy and simple: "I use Synchronization!"
Well, true...but a bit too simple. In many cases the synchronized method or block works, but in complicated environment such as updating in a synchronized method that invokes other methods which could belong to other objects and that could be very tricky.
Huh? Where is the trickiness? The cardinal problem between synchronized block and locked block is that synchronized block or method could cause "starvation" to some other threads. Meaning: if a thread blocks an object with "synchronized" and is somehow run into troubles during the processing phase other threads could wait to death because there is no way to ask a synchronized block or method whether it is in progress or hangs...and your app could "hang" forever with it.
Java offers developers various locking mechanisms which are in the package java.util.concurrent.locks (click HERE for details) and with it developers could get rid of the dangerous "App-Hanging". The second thing is the ExcecutorService pool. Thread and Runnable Implementation are un-reusable and usually greedy for resources. That could cause problems with memory shortage or performant sluggishness. To overcome such problems the basic package java.util.concurrent (click HERE) gives the developers the tools to reuse some tasks (Callable or Runnable) and to share some resources. And that effects tremendously the processing performance.
Let start with the synchronized block or method. If the entire method that contains the objects is synchronized it is the synchronized method. If only the object is synchronized it's the synchronized block. A block is usually an object or a group of several statements. If an object is a primitive (such as int, short, double, etc.) the "this" must be used instead of the variable name.
Java:
int count = 0;
// synvhronized method
public synchronized void counter() {
count++;
}
// synchronized block
public void counter() {
synchronized(this) { // this is used because count is an int or a primitive
count++
}
}
//
ArrayList<String> lst = new ArrayList<>();
public void add(String s) {
synchronized(lst) { // lst is an object
lst.add(s);
}
}
Java:
public class SynLock {
public SynLock( ) { }
public synchronized void counter() {
count++;
}
public int getCounter() {
return count;
}
public long process(int max, boolean fixed) {
pool = fixed? Executors.newFixedThreadPool(100):
Executors.newWorkStealingPool(Runtime.getRuntime().availableProcessors());
ArrayList<Future<?>> fu = new ArrayList<Future<?>>();
long beg = System.currentTimeMillis();
for (int i = 0; i < max; ++i) {
fu.add(pool.submit(()->counter()));
}
try {
for (Future f : fu) f.get();
} catch (Exception ex) { }
beg = System.currentTimeMillis()-beg;
pool.shutdownNow();
return beg;
}
private ExecutorService pool;
private int count = 0;
}


See the differences? And that is the way how the "Competitive Programming" works: same task, different outcomes.
Sửa lần cuối: