Java 无法使用线程同步进程

Java 无法使用线程同步进程,java,multithreading,synchronization,Java,Multithreading,Synchronization,我通过线程行为测试我的技能。当我实现Runnable接口和synchronizedrun方法时,我得到了绝对的结果。但是,当我扩展线程类时,结果是不可预测的。下面是两个案例。我认为,两种情况下的线程使用相同的资源 案例1Runnable class Counter{ int count; public void doCount(){ count=count+1; } public int getCount(){ return cou

我通过
线程
行为测试我的技能。当我实现
Runnable
接口和
synchronized
run方法时,我得到了绝对的结果。但是,当我扩展
线程
类时,结果是不可预测的。下面是两个案例。我认为,两种情况下的线程使用相同的资源

案例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());
    }
}
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也在两个线程之间使用相同的资源,那么为什么它们停止同步。有人能解释一下吗。。我快发疯了

已同步(此)
有故障。在本例中,您有两个实例:o1和02<代码>同步(计数器)
应该可以工作,但不是理想的解决方案

理想情况下,仅为了测试,我会使用私有锁

class Counter{
    final Object lock= new Object();
    int count;
    public void doCount(){
        synchronized (lock){
            count=count+1;
        }
    }
    public int getCount(){
        synchronized (lock) {
            return count;
        }
    }
}

比睡眠更好的方法是使用
Thread.join()

在第二种情况下,底层“this”对象引用一个类的两个不同实例,即o1和o2。for循环在两个不同的实例上独立运行,对象监视器锁定,因此这里的计数器修改显然是不同步的


在这种情况下,标准数据结构通常会抛出ConcurrentModificationException。

当您使用
synchronized(this)
时,它会锁定您调用方法的对象

如果您创建了一个

One one = new One();
并传递给每个方法,以便两者都使用相同的对象作为锁

在案例2中,您正在创建两个对象,并且两个对象都使用不同的对象作为锁。您可以使用计数器作为锁,而不是这个,这将解决您的问题

 One o1 = new One(counter);// using same resource counter
 One o2 = new One(counter);

好的,首先您应该知道实现runnable和从线程继承之间的区别。 然后,阅读以清楚地理解添加synchonized的步骤


无论如何,请检查同步功能中的
。在第一种情况下,它与
One=newone(一)
。。。第二个
One-One=新的一个(One)
它与自身有关。两者都是同步的,但第一个与第一个实例同步。但是,第二个实例与第二个实例同步。尝试使用锁,它应该可以工作。

在本例中,我将只在计数器实例上进行同步,而不是像其他人建议的那样将同步放在计数器类中。这使计数器类保持简单,并且不知道任何潜在的线程问题,这些问题只有在多个线程使用该类时才会出现

因此,代码应该是:

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;
}
public int getCount(){
返回计数;
}
}
公共类CountRunner实现可运行{
柜台;
公共计数员(计数器){
this.counter=计数器;
}
公开募捐{
已同步(计数器){

对于(int i=0;我自己问一下:
synchronized(this)中的
this
是什么
在第一种情况下,第二种情况下是什么。好吧,等等,问..我的意思是思考..因为..你不是在计数器上同步,而是在其他有多个实例的东西上同步?你完全正确。
这是一个实例。你在第一种情况下和第二种情况下创建了多少个
One
的实例nd实例?不。这不是问题。确实有一个实例的两个实例,但都共享同一个计数器。问题是第一个线程在一个实例的第一个实例上同步,第二个线程在一个实例的第二个实例上同步。因此,它们访问共享资源(计数器),但在不同的锁上同步。因此,在第一个线程中输入同步块不会阻止另一个线程输入其同步块。同步doCount()onluy不会使计数器线程安全。getCount()也必须在同一个锁上同步。@JBNizet你是对的;我没有发布完整的代码。这就是为什么我如此喜欢stack overflow社区,因为它们采用开箱即用的方法来解决问题。当两个方法上的synchronized关键字都可以工作时,为什么要引入lock对象?@mlecz我提到了synchronized(counter)也行。我只喜欢第二种one@JSK很高兴你喜欢它们。现在我已经理解了我的疑虑,这似乎也是一种冷静而精确的方式