多个线程访问内部类

时间:2014-02-20 12:28:49

标签: java multithreading

我创建了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

如果有人可以解释在多线程情况下如何处理内部类,那将是非常有帮助的?我们可以同步整个内部阶级吗?

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:4)

班级是否是内部班级是无关紧要的。您在此处看到的是预期的输出:您的程序中没有任何同步,因此线程调度程序可以在需要时自由切换到其他线程。这是一系列可能的操作,这将导致您看到的输出。

  1. T1:增量计数器
  2. T1:concaenate
  3. T1:打印
  4. T3:增量计数器
  5. T3:连接
  6. T2:增量计数器
  7. T2:连接
  8. T2:打印
  9. T3:打印
  10. 如果你想要计数器增量,连接和打印是一个原子操作,那么你应该确保它在一个独特的锁上同步:

    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和线程可见性的问题

<强>读然后变

增量首先是读取,然后是添加,然后是回写。 ++countercounter = 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类中。