我一直认为在实现Runnable的java类中同步run方法是多余的。 我想弄清楚为什么人们会这样做:
public class ThreadedClass implements Runnable{
//other stuff
public synchronized void run(){
while(true)
//do some stuff in a thread
}
}
}
它似乎是多余的和不必要的,因为它们正在为另一个线程获取对象的锁。或者更确切地说,他们明确表示只有一个线程可以访问run()方法。但是由于它的run方法,它本身不是自己的线程吗?因此,只有它可以访问自己,它不需要一个单独的锁定机制?
我在网上发现了一个建议,通过同步run方法,你可能会创建一个事实上的线程队列,例如:
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();
}
我个人从不这样做,但它提出了为什么有人会同步run方法的问题。 任何想法为什么或为什么不应该同步run方法?
答案 0 :(得分:29)
同步run(
的{{1}}方法是完全没有意义的,除非要在多个线程和Runnable >您想要对这些线程的执行进行顺序化。这基本上是一个矛盾。
理论上还有一个更复杂的场景,您可能希望同步Runnable
方法,这又涉及在多个线程之间共享run()
,但也使用Runnable
和wait()
。我从未在21年多的Java中遇到过它。
答案 1 :(得分:2)
使用synchronized void blah()
优于void blah() { synchronized(this) {
有一个好处,那就是你的结果字节码将缩短1个字节,因为同步将是方法签名的一部分而不是单独的操作。这可能会影响JIT编译器内联方法的机会。除此之外没有区别。
最佳选择是使用内部private final Object lock = new Object()
来阻止某人可能锁定您的显示器。它实现了相同的结果,没有邪恶的外部锁定的缺点。你确实有这个额外的字节,但它很少有所作为。
所以我会说不,不要在签名中使用synchronized
关键字。相反,使用像
public class ThreadedClass implements Runnable{
private final Object lock = new Object();
public void run(){
synchronized(lock) {
while(true)
//do some stuff in a thread
}
}
}
}
编辑以回应评论:
考虑同步的作用:它阻止其他线程进入相同的代码块。所以想象你有一个类似下面的类。假设当前大小为10.有人试图执行添加并强制调整后备阵列的大小。当他们正在调整数组大小时,有人会在另一个线程上调用makeExactSize(5)
。现在突然间你正试图访问data[6]
并且它会炸毁你。同步应该可以防止这种情况发生。在多线程程序中,您只需要进行NEED同步。
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;
}
}
答案 2 :(得分:2)
为什么呢?最小的额外安全性,我认为没有任何合理的情况可以产生影响。
为什么不呢?这不标准。如果您是作为团队的一部分进行编码,当其他成员看到您的同步run
时,他可能会浪费30分钟来试图找出您run
或框架中的特殊内容用于运行Runnable
。
答案 3 :(得分:1)
根据我的经验,将“synchronized”关键字添加到run()方法中没有用。如果我们需要同步多个线程,或者我们需要一个线程安全的队列,我们可以使用更合适的组件,例如ConcurrentLinkedQueue。
答案 4 :(得分:0)
那么理论上你可以毫无问题地调用run方法本身(毕竟它是公共的)。但这并不意味着应该这样做。所以基本上没有理由这样做,除了给调用run()的线程增加可忽略的开销。好吧,除非你多次使用实例调用new Thread
- 虽然我是a)不确定这对于线程API合法而且b)似乎完全没用。
此外,createThreadQueue
无效。非静态方法上的synchronized
在实例对象上同步(即this
),因此所有三个线程将并行运行。
答案 5 :(得分:0)
浏览代码注释并取消注释并运行不同的块以清楚地看到差异,注意同步只有在使用相同的runnable实例时才有区别,如果每个线程启动得到一个新的runnable它会赢得&t; t有所作为。
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) {}
}
}
}
答案 6 :(得分:-2)
实际上很容易证明“同步或不同步”
如果你的方法调用可以改变对象的内部状态,那么“同步”否则不需要
简单的例子
public class Counter {
private int count = 0;
public void incr() {
count++;
}
public int getCount() {
return count;
}
}
在上面的示例中,incr()需要同步,因为它将更改count的值,而不需要getCount()同步
然而还有另一个极端情况,如果计数是java.lang.Long,Double,Object那么你需要声明为
private volatile long count = 0;
确保ref update是原子的
基本上这就是你在处理多线程时需要考虑99%的时间