多线程访问方法

时间:2014-11-03 13:08:57

标签: java

我'我正在学习多线程,对于一些事情我有点困惑,

例如

public class Example{

   public void function1(){
        int toPrint = 0;
        System.out.println(toPrint);
        for(int i = 0; i < 10; i++)
        System.out.println("hello stackOverflow!");
   }
   public syncrhonized void function2(){
       ...
   }

}

public static void main(String[] args) {
            Example example = new Example();
            for (int i = 0; i < 10; i++) {
                RunningThread thread = new RunningThread(example);
                thread.start();
            }
        }

和这样的循环

public class RunningThread{
    Example instanceClass;
    public RunningThread(Example instanceClass){
     this.instanceClass = instanceClass;
    public void run(){
        while(true){
           instanceClass.function1();
           instanceClass.function2();
        }
    }

现在我无法显示图像,但我想明确我的配音

如果我开始N个线程,我必须打算这种情况

 _______________________________         ______________________________
|             thread1           |       |         thread..N            |
.................................       ................................
|            function1          |       |          function1           |
|          int toPrint = 0;     |       |        int toPrint = 0;      |
| System.out.println(toPrint);  |       |  System.out.println(toPrint);|
| for(int i = 0; i < 10; i++)   |       |  for(int i = 0; i < 10; i++) |
|    System.out.println();      |       |     System.out.println();    |
---------------------------------       --------------------------------

我的意思是,每个线程都有自己的流程(他自己&#34;复制&#34; of function1 ),完成后他们将等待执行锁定function2()

 _______________________________        
|       thread1 and thread..N   |       
.................................      
|            function1          |      
|          int toPrint = 0;     |       
| System.out.println(toPrint);  |     
| for(int i = 0; i < 10; i++)   |      
|    System.out.println();      |       
--------------------------------- 

以这种方式每个线程shares具有相同的功能和内容 (所以例如一个线程初始化值而另一个线程没有初始化它) 并在完成后等待执行锁定的function2()

执行顺序将始终受到尊重,首先是function1和function2?

对不起,如果这么久,无论如何都要提前感谢。

2 个答案:

答案 0 :(得分:4)

synchronized关键字不会锁定函数,而是锁定对象,这意味着两个线程不能同时使用同一个对象。 <{1}}只是

的语法糖
synchronized void function2

同步(即锁定对象)的原因是没有线程可以在其不变量被破坏的状态下看到对象。在您的示例中,类void function2(){synchronized(this){// 没有状态,因此没有不变量,这意味着您不需要锁定它。

您似乎关注的是Example的局部变量。但是,局部变量永远不会在线程之间共享,因此每个线程都有自己的每个局部变量的实例。


附录:根据用户 hexafraction 的建议,需要同步的示例:

考虑以下简单类:

function2

这个类是可变的;其状态由public class Example { public int count = 0; public void increment() { count++; } public void decrement() { count--; } } 的值定义。如果客户端调用countincrement,则状态应该更改。两种方法都有契约:

  • decrement必须保证increment的值是count的旧值加1。我们用count

  • 表示这个合同
  • Simliarly,count = old(count) + 1的合约是decrement

让我们依次运行这个课程:

count = old(count) - 1

打印:

public static void main(String[] args) {
    Example sharedData = new Example();
    for (int i = 0; i < 1000; i++)
        sharedData.increment();
    System.out.println("Incrementer finished");
    for (int i = 0; i < 1000; i++)
        sharedData.decrement();
    System.out.println("Decrementer finished");
    System.out.println(sharedData.count);
}

我们可以根据需要运行代码,结果将始终相同。

让我们定义多个同时使用类Incrementer finished Decrementer finished 0 同一实例的线程:

Example

我们现在有两个线程:增量器和减量器。代码看起来有点不同,但我们可能期望它达到相同的结果。我们再次在共享public static void main(String[] args) throws InterruptedException { Example sharedData = new Example(); Thread incrementer = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) sharedData.increment(); System.out.println("Incrementer finished"); } }); Thread decrementer = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) sharedData.decrement(); System.out.println("Decrementer finished"); } }); incrementer.start(); decrementer.start(); incrementer.join(); decrementer.join(); System.out.println(sharedData.count); } 上拨打incrementdecrement两次。但现在,结果完全不确定。多次运行代码,打印的数字可能是:sharedData

这怎么可能?我们总是添加一个或减去一个,但在完成1000次后,我们应该得到值0,对吗?问题是一个线程可能对另一个线程的更改无知。请注意16, -76, 138, -4不是原子的;它与count++相同,它由读取,计算和写入组成。

考虑以下连续历史记录:

count = count + 1

请注意,incrementer enters increment and reads the value of count, count == 0 decrementer enters decrement and reads the value of count, count == 0 incrementer adds one and modifies the state, count == 1 decrementer subtracts one and modifies the state, count == -1 计算的状态更改基于其读取的decrementer的值,即count,这意味着它没有看到状态更改由0

有多种方法可以解决此问题,但让我们尝试使用incrementer关键字。我们可以通过锁定实例来禁止对synchronized的共享实例进行并发修改。所以我们修改我们的课程:

Example

让两个方法锁定实例很重要,因为两个方法都不能看到处于不一致状态的对象。

如果我们无法修改public class Example { public int count = 0; public synchronized void increment() { count++; } public synchronized void decrement() { count--; } } 的代码,例如,因为它是我们使用的库的一部分,该怎么办?我们如何使用Example关键字来使用多个线程的代码?如前所述,synchronizedsynchronized void increment(){相同,因此同步不是方法的属性,而是对象的属性。保留void increment(){synchronized(this)的代码不变,我们可以改变我们的客户端:

Example

答案 1 :(得分:0)

由于你同时制作了function1function2 synchronized 没有线程可以在执行结束之前留下它们,所以一个线程无法停止function1和另一个线程的中间执行其function1或function2。但是请注意,如果两个线程中都存在相同的run()方法(我猜是这样),而第一个完成function1,则调度程序或调度程序可以停止它并运行thread2,它可以完成两个函数调用,然后thread1可以继续。

注意:使用methods()而不是函数()。