简体   繁体   English

Java中的同步线程

[英]Synchronized Thread in Java

I have 3 class like this:我有 3 节课是这样的:

Source.java源码.java

public class Source extends Thread{
    private int x= 0;
    public void increment(int id){
          x++;
          System.out.println(id+" "+x);
    }
}

Task.java任务.java

public class Task extends Thread{
      private Source source;
      private int id;
      public Task(Source source, int id){
            this.source=source;
            this.id=id;
      }
      public void run(){
             for (int i=0;i<100;i++){ 
                 try{Thread.sleep(1000);}catch(InterruptedException e){}
                 source.inc(id);
             }
      }
}

Main.java主.java

public class Main{
      public static void main(String[] args) throws IOException{
             Source source = new Source();
             Task t1=new Task(source,1);
             Task t2=new Task(source,2);
             t1.start();
             t2.start();
      }
}

I want when the x of the class Source will be equal to 4 only one task continues to increment x until x is equal to 8, we return to normal.我希望当 Source 类的 x 等于 4 时,只有一个任务继续增加 x 直到 x 等于 8,我们恢复正常。 The result will look like this:结果将如下所示:

1 1
2 2
1 3
2 4
1 5
1 6
1 7
1 8
1 9
1 10
2 11
1 12
2 13
...

How do I modify the code to achieve the desired result?如何修改代码以达到预期的效果?

Basically you have two threads that modify the same variable x.基本上你有两个线程修改同一个变量 x。 There is no garantee about the order of execution.没有关于执行顺序的保证。 You should synchronize.你应该同步。

With your current implementation you may face a problem (The race condition problem): Race condition example使用您当前的实现,您可能会遇到一个问题(竞争条件问题): 竞争条件示例

Something like this is an scenario that most likely is going to happen to you:像这样的事情很可能会发生在你身上:

....
1 3
2 4
2 5
1 6
1 7
2 7
1 8
2 9
1 10
2 10
...

As you can see the thread 2 (source 2) tries to increment the variable x when the variable has been already incremented but the value it has to increment is the old one.如您所见,线程 2(源代码 2)尝试在变量 x 已经递增但它必须递增的值是旧值时递增变量 x。

x = 0 x = 0

  1. Thread 1 reads the variable x (0)线程 1 读取变量 x (0)
  2. Thread 2 reads the variable x (0)线程 2 读取变量 x (0)
  3. Thread 1 increments variable x + 1 (0 + 1) = 1线程 1 递增变量 x + 1 (0 + 1) = 1
  4. Thread 2 increments variable x + 1 (0 + 1) = 1线程 2 递增变量 x + 1 (0 + 1) = 1

In order to solve this you need to synchronize your variable, an AtomicInteger would be enough.为了解决这个问题,你需要同步你的变量,一个 AtomicInteger 就足够了。 + I don't think you need the extends Thread on your Source class, you can get rid of it + 我认为你的 Source 类不需要 extends Thread,你可以去掉它

There are a few things off within your code.您的代码中有一些内容。 As it has been pointed out in the comments and in @Vml11's answer, your shared resource Source does not need to extend the Thread class and you definitely need to implement a synchronized approach to access and modify x from two different threads in order to not cause Thread Interference.正如评论和@Vml11 的回答中指出的那样,您的共享资源Source不需要扩展Thread类,您肯定需要实现同步方法来访问和修改来自两个不同线程的 x ,以免导致螺纹干涉。

https://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html https://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html

In fact, right now both threads can access Source not in exclusive way.事实上,现在两个线程都可以访问Source而不是独占方式。 This means that the changes performed by a thread can be overlapped and lost by the operations executed by the other one, since there is no happens-before relationship between the two threads, ei the changes made by a thread might not be visible by the other.这意味着一个线程执行的更改可能会被另一个线程执行的操作重叠和丢失,因为两个线程之间没有happens-before的关系,一个线程所做的更改可能对另一个线程不可见.

https://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html https://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html

In order to establish a happens-before relationship between the threads, you need to implement a synchronized access to Source , for example by adding the synchronized keyword to the increment method.为了在线程之间建立happens-before关系,您需要实现对Source的同步访问,例如通过将synchronized关键字添加到increment方法。 Your fixed code would look like this (just for conciseness I've reduced the number of iterations from 100 to 10).您的固定代码看起来像这样(为了简洁起见,我将迭代次数从 100 减少到 10)。

public class Main {
    public static void main(String[] args) {
        Source source = new Source();
        Task t1 = new Task(source, 1);
        Task t2 = new Task(source, 2);
        t1.start();
        t2.start();
    }
}

class Source {
    private int x = 0;

    //Field to establish by the thread's id who can update x once it reaches 4
    private int idThreadExclusiveInc = -1;

    public synchronized void increment(int id) {
        //The current thread can update x only if the increment operation isn't exclusive to a thread or if the current thread can update x
        if (idThreadExclusiveInc == -1 || idThreadExclusiveInc == id) {
            x++;

            //If x is equal to 4 then the current thread sets its id to exclusively increment x until it reaches 8
            idThreadExclusiveInc = x == 4 ? id : idThreadExclusiveInc;

            //If x is equal to 8 the var holding the thread's id for exclusive increment is reset
            idThreadExclusiveInc = x == 8 ? -1 : idThreadExclusiveInc;

            System.out.printf("id: %d - x: %d%n", id, x);
        }
    }
}

class Task extends Thread {
    private Source source;
    private int id;

    public Task(Source source, int id) {
        this.source = source;
        this.id = id;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
            source.increment(id);
        }
    }
}

Here is a link to test the code above:这是测试上述代码的链接:

https://www.jdoodle.com/iembed/v0/rT5 https://www.jdoodle.com/iembed/v0/rT5

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM