我尝试学习一些有关多线程的知识,并在多线程计数器上进行了尝试。就我所知,我同步了计数器变量,但是遇到以下问题:
谁能解释,我做错了吗?
类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;
}
}
答案 0 :(得分:3)
这说明了吞咽异常的陷阱。如果您在线程中捕获了异常并仅将其输出,则会观察到以下情况:
java.lang.IllegalMonitorStateException
at java.base/java.lang.Object.notifyAll
at Thread2.run
当您在不持有锁的对象(在本例中为wait
对象)上调用notify
和notifyAll
/ 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)
增量和打印在两个单独的操作中完成。
因此您可以拥有(例如)
关于交替轮换:两个线程都在自己(因此,在两个单独的对象上)上调用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。不要公开。