我正在测试我的技能而不是Thread
行为。当我实现Runnable
接口和synchronized
run方法时,我得到了绝对的结果。但是,当我延长Thread
课程时,结果是不可预测的。以下是两个案例。我认为,两种情况下的线程都使用相同的资源。
案例1 Runnable
class Counter{
int count;
public void doCount(){
count=count+1;
}
public int getCount(){
return count;
}
}
public class One implements Runnable{
Counter counter = new Counter(); // common object to be shared with two threads
public void run(){
synchronized (this) {
for(int i=0;i<10000;i++){
counter.doCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
One one = new One();
Thread t1 = new Thread(one);// using same resource 'one'
Thread t2 = new Thread(one);// using same resource 'one'
t1.start();
t2.start();
Thread.sleep(2000); // to give both threads time to complete
System.out.println(one.counter.getCount());
}
}
案例2 Thread
class Counter{
int count;
public void doCount(){
count=count+1;
}
public int getCount(){
return count;
}
}
public class One extends Thread{
Counter counter; //common object to be shared with two threads
One(Counter counter){
this.counter = counter;
}
public void run(){
synchronized (this) {
for(int i=0;i<10000;i++){
counter.doCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
One o1 = new One(counter);// using same resource counter
One o2 = new One(counter);// using same resource counter
o1.start();
o2.start();
Thread.sleep(2000); // to give both threads time to complete
System.out.println(counter.getCount());
}
}
对于案例1,我每次输出20000。但对于案例2,我每次都会得到随机值。为什么会这样?案例2也在两个线程中使用相同的资源,那么为什么它们停止获取synchronized
。任何人都可以解释这个......我疯了!
答案 0 :(得分:2)
在这个例子中,我只是在计数器实例上进行同步,而不是按照其他人的建议将同步放在Counter类中。这使Counter类变得简单并且不知道任何潜在的线程问题,这些问题只有在多个线程使用该类时才会出现。
所以代码是:
public class Counter {
int count;
public void doCount() {
count=count+1;
}
public int getCount() {
return count;
}
}
public class CountRunner implements Runnable {
Counter counter;
public CountRunner(Counter counter){
this.counter = counter;
}
public void run() {
synchronized (counter) {
for(int i=0;i<10000;i++){
counter.doCount();
}
}
}
}
public class CountThread extends Thread {
Counter counter;
public CountThread(Counter counter){
this.counter = counter;
}
public void run() {
synchronized (counter) {
for(int i=0;i<10000;i++){
counter.doCount();
}
}
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
countRunnerTest();
countThreadTest();
}
public static void countRunnerTest() {
Counter counter = new Counter();
CountRunner countRunner = new CountRunner(counter);
Thread t1 = new Thread(countRunner);
Thread t2 = new Thread(countRunner);
t1.start();
t2.start();
Thread.sleep(2000);
System.out.printf("CountRunnerTest result=%s", counter.getCount());
}
public static void countThreadTest() {
Counter counter = new Counter();
CountThread t1 = new CountThread(counter);
CountThread t2 = new CountThread(counter);
t1.start();
t2.start();
Thread.sleep(2000);
System.out.printf("CountThread result=%s", counter.getCount());
}
}
答案 1 :(得分:1)
synchronized (this)
有问题。在这种情况下,您有2个实例:o1和02. synchronized (counter)
应该可以工作,但不是理想的解决方案。
理想情况下,只是为了测试,我会使用私人锁。
class Counter{
final Object lock= new Object();
int count;
public void doCount(){
synchronized (lock){
count=count+1;
}
}
public int getCount(){
synchronized (lock) {
return count;
}
}
}
使用sleep
Thread.join()
更好的方法
答案 2 :(得分:1)
在第二种情况下,基础“this”对象指的是One类的两个不同实例,即o1和o2。 for循环独立运行,在两个不同的实例上有对象监视器锁,因此很明显这里的计数器修改是非同步的。
通常,标准数据结构会在这种情况下抛出ConcurrentModificationException。
答案 3 :(得分:1)
当您使用synchronized (this)
时,它会锁定您调用方法的对象。
在案例一中你创建
One one = new One();
并传递给每个方法,因此两者都使用相同的对象作为锁。
在案例2中,您创建了两个对象,并且两个对象都使用不同的对象作为锁。您可以使用计数器作为锁而不是此,这将解决您的问题。
One o1 = new One(counter);// using same resource counter
One o2 = new One(counter);
答案 4 :(得分:1)