大家好我正在做一个简单的程序来计算从0到99的数字之和。我正在阅读java中的线程,我正在尝试理解它是如何工作的,特别是多线程所以我写了一个简单的程序来理解概念。但是我的程序的输出是不同的,因为它输出0和4950.似乎有2个线程正在运行?主线程然后对象线程一个?我输出有问题导致它们不同步。我希望我走在正确的轨道上,但我不确定并需要指导。有人可以解释如何使用synchronized来解决这个问题。难以理解吗?
public class Testing {
public static void main(String[] args) {
ThreadB b = new ThreadB();
Thread a = new Thread(b);
a.start();
System.out.println(b.total);
}
}
class ThreadB extends Thread {
int total;
public ThreadB() {
this.total = 0;
}
public synchronized int total() {
for(int i = 0; i < 100; i++) {
total += i;
}
return total;
}
public void run() {
System.out.println(total());
}
}
答案 0 :(得分:1)
你有两个主题:主线程和线程'a'。 您有另一个对象b,它继承自Thread,但您将其视为int。
运行执行求和的线程a,然后打印结果。 然后打印存储的b中的值。
同步没有问题,因为没有一个线程共享相同的变量。整数在a和b之间是不同的。如果要创建问题,请将int作为Testing的成员。
答案 1 :(得分:0)
当两个或多个线程尝试访问同一资源并且您希望确保它们不会同时执行同步时,需要同步。从写入的角度来看,这通常很关键,但从读取的角度来看则很少(即:多个线程可以同时读取一个值,而不是在更新的同时,或者没有多个线程更新相同的资源同时)。
在您的示例中,您实际上只有一个线程正在运行。我不怀疑这是意图。
有主线程和线程a
。我重新组织了你的代码而没有改变任何东西,但希望更清楚:
public class Testing {
public static void main(String[] args) {
ThreadB b = new ThreadB();
System.out.println(b.total);
Thread a = new Thread(b);
a.start();
}
}
主线程实际上并没有做任何事情。它只是创建一个新对象b,并打印出b.total(因此得到的0值)。至少,你也应该打电话给b.start()
。
当您致电a.start()
时,您启动的计算线程已完成。这部分在ThreadB()对象中启动计算,打印计算总数。
如果您真的想测试并发性,那么您需要拥有多个线程访问的共享资源。但是,在您的情况下,由于total
是私有实例成员,因此线程a和b将具有total
的不同实例,并且没有任何同步问题。 synchronized
语句是多余的,因为每个线程都在调用它自己的total()
方法。
http://www.tutorialspoint.com/java/java_thread_synchronization.htm有一个很好的解释/示例同步和多线程。
答案 2 :(得分:0)
为了清楚起见,这里只有两个主题。 JVM提供了用于执行main方法的线程。然后有一个名为a
的局部变量引用一个Thread对象。局部变量b
引用一个Thread,但它不用作一个线程,因为它从来没有调用它。所有线程实现Runnable,将b
传递给线程a
的构造函数意味着b
被a
用作Runnable。所以线程a
运行,执行b
中定义的run方法。
当a
执行b
的run方法时,b
上的锁定是无竞争的。程序中没有其他任何东西试图获取b
上的锁定。 total()上的synchronized
关键字仅影响调用total()方法的线程,因此没有任何东西阻止main方法访问名为total的b
实例成员。因此主要方法继续进行并访问total,此时尚未更新,因为线程a
仍在开始。
如果你完全私有,并添加了ThreadB的访问者:
public synchronized int getTotal() { return total;}
然后调用此getter将强制调用者获取锁(因为这里的synchronized意味着“使用此方法作为锁定调用实例”,因此total()方法和新的getTotal()方法将共享在获取总值之前,相同的锁)。然后,一旦线程a
进入其run方法,它将获得b
上的锁,并且主线程必须等到它才能获得b
上的锁以便访问总数
即使你这样做,但主线程可能仍然能够访问b
并在线程a
开始之前获取锁定并获得{{1}上的锁定}。主线程和线程b
之间存在竞争条件,主线程具有优势,因为它已经启动并处于活动状态(而starting a new thread is expensive)。在获取总数之前,您可以将Thread.sleep添加到main方法,以便a
线程有机会首先获取锁。