计数器同步

时间:2018-11-25 19:46:46

标签: java

我尝试学习一些有关多线程的知识,并在多线程计数器上进行了尝试。就我所知,我同步了计数器变量,但是遇到以下问题:

  1. 螺纹不交替转动
  2. 计数器未按预期计数(例如从337到339或从344到344)

谁能解释,我做错了吗?

类RunThreads

public class RunThreads {    
    public static void main(String[] args) {
    Thread thread1 = new Thread1();        
    Thread thread2 = new Thread2();

    thread1.start();
    thread2.start();                    
    }
}

Class Thread1

public class Thread1 extends Thread {
public void run(){
    while (ThreadCount.counter < 1000){
        ThreadCount.incrementCounter();
        ThreadCount.printCounter(this);

        try{
            notifyAll();
            wait();

       }
       catch (Exception e){}
    }
}
}

Thread2类(是的,我不需要两个单独的类,但是它使我更容易理解)

public class Thread2 extends Thread {
public void run(){
    while (ThreadCount.counter < 1000){
        ThreadCount.incrementCounter();            
        ThreadCount.printCounter(this);

        try{
            notifyAll();
            wait();   

       }
       catch (Exception e){}
    }
}
}

类ThreadCount

public class ThreadCount {    

public static int counter = 0;

public static synchronized void  incrementCounter(){
    counter++;

}

public static void decrementCounter(){
    counter--;
}

public static synchronized void  printCounter(Thread t){;      
    Output.append(t.getName() + ":" + counter + "\n");
}    
}

类输出

public class Output{
public static String value = "";     

public static synchronized void append(String s) {
    value+=s;
}
 }

3 个答案:

答案 0 :(得分:3)

这说明了吞咽异常的陷阱。如果您在线程中捕获了异常并仅将其输出,则会观察到以下情况:

java.lang.IllegalMonitorStateException
    at java.base/java.lang.Object.notifyAll
    at Thread2.run

当您在不持有锁的对象(在本例中为wait对象)上调用notifynotifyAll / Thread时,会发生这种情况通过synchronized

如果创建两个线程都同步的公共对象,然后调用wait / notifyAll,您将得到所观察到的结果。例如:

class Thread1 extends Thread {
    public void run(){
        synchronized (ThreadCount.lockObj) {
            while (ThreadCount.counter < 1000) {
                ThreadCount.incrementCounter();
                ThreadCount.printCounter(this);

                try {
                    ThreadCount.lockObj.notifyAll();
                    ThreadCount.lockObj.wait();

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

答案 1 :(得分:1)

增量和打印在两个单独的操作中完成。

因此您可以拥有(例如)

  • 线程1:从342增加到343
  • 线程2:从343增加到344
  • 线程2:打印344
  • 线程1:打印344

关于交替轮换:两个线程都在自己(因此,在两个单独的对象上)上调用wait()和notifyAll(),并且这样做时未持有其锁,这将引发异常。而且,由于您忽略异常,因此您不会注意到所犯的错误。永远不要忽略异常。不要捕获异常。

要交替轮流,incrementCounter()方法应如下所示:

private static boolean turn;

public static synchronized void incrementCounter(){
    counter++;
    printCounter(Thread.currentThread());

    turn = !turn;
    boolean nextTurn = !turn;

    ThreadCount.class.notifyAll();
    while (turn != nextTurn) {
        try {
            ThreadCount.class.wait();
        }
        catch (InterruptedException e) {
            return;
        }
    }
}

turn boolean和while循环可能看起来很平均,但是实际上,如果您希望代码能够按预期工作,即使在发生虚假唤醒的情况下,它们实际上也是必需的。

答案 2 :(得分:0)

typedef void (*fn_ptr_type)();

template<unsigned N> struct array {
    typedef const fn_ptr_type (&type)[N];
};
constexpr fn_ptr_type a[] { nullptr };

template<typename array<1>::type> struct S {};
template<typename array<1>::type> void tf() {}

int main() {
    tf<a>(); //both gcc (gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516) and VS (Version 19.16.27023.1 for x64) - ok
    S<a>(); //gcc - ok, VS - compiler error:
            //Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27023.1 for x64
            //Copyright (C) Microsoft Corporation.  All rights reserved.
            //
            //test.cpp
            //test.cpp(13): error C2975: 'S': invalid template argument for 'unnamed-parameter', expected compile-time constant expression
            //test.cpp(8): note: see declaration of 'S'
            //test.cpp(13): error C2440: 'specialization': cannot convert from 'int' to 'void (__cdecl *const (&)[1])(void)'
            //test.cpp(13): note: Reason: cannot convert from 'int' to 'const fn_ptr_type [1]'
            //test.cpp(13): note: There are no conversions to array types, although there are conversions to references or pointers to arrays
            //test.cpp(13): note: see reference to class template instantiation 'S<0>' being compiled
            //test.cpp(13): error C2973: 'S': invalid template argument 'int'
            //test.cpp(8): note: see declaration of 'S'
    return 0;
}

在此操作中,线程1递增2,线程2递增0。

您可以将AtomicInteger等用于线程安全操作。

您应该封装int。不要公开。