可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have always thought that synchronizing the run method in a java class which implements Runnable is redundant. I am trying to figure out why people do this:
public class ThreadedClass implements Runnable{
//other stuff
public synchronized void run(){
while(true)
//do some stuff in a thread
}
}
}
It seems redundant and unnecessary since they are obtaining the object's lock for another thread. Or rather, they are making explicit that only one thread has access to the run() method. But since its the run method, isn't it itself its own thread? Therefore, only it can access itself and it doesn't need a separate locking mechanism?
I found a suggestion online that by synchronizing the run method you could potentially create a de-facto thread queue for instance by doing this:
public void createThreadQueue(){
ThreadedClass a = new ThreadedClass();
new Thread(a, "First one").start();
new Thread(a, "Second one, waiting on the first one").start();
new Thread(a, "Third one, waiting on the other two...").start();
}
I would never do that personally, but it lends to the question of why anyone would synchronize the run method. Any ideas why or why not one should synchronize the run method?
回答1:
Synchronizing the run(
) method of a Runnable
is completely pointless unless you want to share the Runnable
among multiple threads and you want to sequentialize the execution of those threads. Which is basically a contradiction in terms.
There is in theory another much more complicated scenario in which you might want to synchronize the run()
method, which again involves sharing the Runnable
among multiple threads but also makes use of wait()
and notify()
. I've never encountered it in 21+ years of Java.
回答2:
There is 1 advantage to using synchronized void blah()
over void blah() { synchronized(this) {
and that is your resulting bytecode will be 1 byte shorter, since the synchronization will be part of the method signature instead of an operation by itself. This may influence the chance to inline the method by the JIT compiler. Other than that there is no difference.
The best option is to use an internal private final Object lock = new Object()
to prevent someone from potentially locking your monitor. It achieves the same result without the downside of the evil outside locking. You do have that extra byte, but it rarely makes a difference.
So I would say no, don't use the synchronized
keyword in the signature. Instead, use something like
public class ThreadedClass implements Runnable{
private final Object lock = new Object();
public void run(){
synchronized(lock) {
while(true)
//do some stuff in a thread
}
}
}
}
Edit in response to comment:
Consider what synchronization does: it prevents other threads from entering the same code block. So imagine you have a class like the one below. Let's say the current size is 10. Someone tries to perform an add and it forces a resize of the backing array. While they're in the middle of resizing the array, someone calls a makeExactSize(5)
on a different thread. Now all of a sudden you're trying to access data[6]
and it bombs out on you. Synchronization is supposed to prevent that from happening. In multithreaded programs you need simply NEED synchronization.
class Stack {
int[] data = new int[10];
int pos = 0;
void add(int inc) {
if(pos == data.length) {
int[] tmp = new int[pos*2];
for(int i = 0; i < pos; i++) tmp[i] = data[i];
data = tmp;
}
data[pos++] = inc;
}
int remove() {
return data[pos--];
}
void makeExactSize(int size) {
int[] tmp = new int[size];
for(int i = 0; i < size; i++) tmp[i] = data[i];
data = tmp;
}
}
回答3:
Why? Minimal extra safety and I don't see any plausible scenario where it would make a difference.
Why not? It's not standard. If you are coding as part of a team, when some other member sees your synchronized run
he'll probably waste 30 minutes trying to figure out what is so special either with your run
or with the framework you are using to run the Runnable
's.
回答4:
From my experience, it's not useful to add "synchronized" keyword to run() method. If we need synchronize multiple threads, or we need a thread-safe queue, we can use more appropriate components, such as ConcurrentLinkedQueue.
回答5:
Well you could theoretically call the run method itself without problem (after all it is public). But that doesn't mean one should do it. So basically there's no reason to do this, apart from adding negligible overhead to the thread calling run(). Well except if you use the instance multiple times calling new Thread
- although I'm a) not sure that's legal with the threading API and b) seems completely useless.
Also your createThreadQueue
doesn't work. synchronized
on a non-static method synchronizes on the instance object (ie this
), so all three threads will run in parallel.
回答6:
Go through the code comments and uncomment and run the different blocks to clearly see the difference, note synchronization will have a difference only if the same runnable instance is used, if each thread started gets a new runnable it won't make any difference.
class Kat{
public static void main(String... args){
Thread t1;
// MyUsualRunnable is usual stuff, only this will allow concurrency
MyUsualRunnable m0 = new MyUsualRunnable();
for(int i = 0; i < 5; i++){
t1 = new Thread(m0);//*imp* here all threads created are passed the same runnable instance
t1.start();
}
// run() method is synchronized , concurrency killed
// uncomment below block and run to see the difference
MySynchRunnable1 m1 = new MySynchRunnable1();
for(int i = 0; i < 5; i++){
t1 = new Thread(m1);//*imp* here all threads created are passed the same runnable instance, m1
// if new insances of runnable above were created for each loop then synchronizing will have no effect
t1.start();
}
// run() method has synchronized block which lock on runnable instance , concurrency killed
// uncomment below block and run to see the difference
/*
MySynchRunnable2 m2 = new MySynchRunnable2();
for(int i = 0; i < 5; i++){
// if new insances of runnable above were created for each loop then synchronizing will have no effect
t1 = new Thread(m2);//*imp* here all threads created are passed the same runnable instance, m2
t1.start();
}*/
}
}
class MyUsualRunnable implements Runnable{
@Override
public void run(){
try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}
class MySynchRunnable1 implements Runnable{
// this is implicit synchronization
//on the runnable instance as the run()
// method is synchronized
@Override
public synchronized void run(){
try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}
class MySynchRunnable2 implements Runnable{
// this is explicit synchronization
//on the runnable instance
//inside the synchronized block
// MySynchRunnable2 is totally equivalent to MySynchRunnable1
// usually we never synchronize on this or synchronize the run() method
@Override
public void run(){
synchronized(this){
try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}
}
回答7:
actually it is really easy to justify "sync or not to sync"
if your method invocation can mutate the internal state of your object, then "sync" otherwise no need
simple example
public class Counter {
private int count = 0;
public void incr() {
count++;
}
public int getCount() {
return count;
}
}
in the example above, incr() needs to be synced, since it will change the val of count, while getCount() synchronization is not necessary
however there is another corner case, if the count is java.lang.Long, Double, Object then you need to declare as
private volatile long count = 0;
to make sure the ref update is atomic
basically that's what you need to think about 99% of time when dealing with multithreading