我创建了3个正在访问MyInnerClass
外部类的内部ThreadsAroundInnerClasses
个线程的线程。
package com.test; public class ThreadsAroundInnerClasses { public static void main(String[] args) { Thread t1 = new Thread(new MyThread(), "THREAD-1"); Thread t2 = new Thread(new MyThread(), "THREAD-2"); Thread t3 = new Thread(new MyThread(), "THREAD-3"); t1.start(); t2.start(); t3.start(); } static class MyInnerClass { static int counter = 0; public void printIt(String threadName) { System.out.println("I am inside inner class, counter value is " + ++counter + " and thread name is " + threadName); } } } class MyThread implements Runnable { @Override public void run() { ThreadsAroundInnerClasses.MyInnerClass innerObj = new ThreadsAroundInnerClasses.MyInnerClass(); innerObj.printIt(Thread.currentThread().getName()); } }
在输出中,我可以看到counter
类中的MyInnerClass
静态变量没有按顺序更新。
I am inside inner class, counter value is 1 and thread name is THREAD-1 I am inside inner class, counter value is 3 and thread name is THREAD-2 I am inside inner class, counter value is 2 and thread name is THREAD-3
如果有人可以解释在多线程情况下如何处理内部类,那将是非常有帮助的?我们可以同步整个内部阶级吗?
提前感谢您的帮助。
答案 0 :(得分:4)
班级是否是内部班级是无关紧要的。您在此处看到的是预期的输出:您的程序中没有任何同步,因此线程调度程序可以在需要时自由切换到其他线程。这是一系列可能的操作,这将导致您看到的输出。
如果你想要计数器增量,连接和打印是一个原子操作,那么你应该确保它在一个独特的锁上同步:
synchronized MyInnerClass.class {
System.out.println("I am inside inner class, counter value is " + ++counter + " and thread name is " + threadName);
}
答案 1 :(得分:1)
试试这个:
static class MyInnerClass {
static int counter = 0;
public void printIt(String threadName) {
synchronized(MyInnerClass.class) {
System.out.println("I am inside inner class, counter value is " + ++counter + " and thread name is " + threadName);
}
}
}
您的计数器是静态的,因此您需要在整个类对象上进行同步。
答案 2 :(得分:1)
这是一个关于read-then-change和线程可见性的问题
<强>读然后变强>
增量首先是读取,然后是添加,然后是回写。 ++counter
是counter = counter + 1
的简写,因此意味着:“读取计数器,添加一个,将结果写回计数器”。这个流可以在另一个线程中间中断:Thread1读取计数器,得到1 - 并被中断。 Thread2读取计数器 - 它仍然是1 - 加1 - 并被中断。 Thread3读取计数器 - 它仍然是1 - ....依此类推。这种行为基本上是随机的。您可以通过在后台运行其他应用程序来激发不同的输出。
线程可见性
对于多核架构上的非易失性,非同步变量,不同的线程可能在不同的核上运行,并且核可以将变量缓存在其寄存器上。这意味着一个线程可能无法看到另一个线程写入变量的内容 - 直到它被提交回内存并且有一个新的写入。
同步是创建内存障碍的一种方法。系统将保证在下次锁定时可以看到在锁中写入的所有内容。实际上 - 当您退出同步块时,您所做的一切都将被提交到内存中。然后你输入同步块,所有内容都从内存中读回。
volatile
关键字告诉系统禁止将变量缓存在寄存器中 - 必须始终在内存中读取和写入。这使得每个人都阅读了最新的内容,但由于上述原因,它无法解决您的问题。
简单的解决方案
解决特定问题的最简单方法是使用AtomicInteger。
static class MyInnerClass {
static final AtomicInteger counter = new AtomicInteger(0);
public void printIt(String threadName) {
System.out.println("I am inside inner class, counter value is " +
counter.incrementAndGet() + " and thread name is " + threadName);
}
}
这将为您节省同步的所有麻烦 - 它包含在AtomicInteger类中。