Java线程不会同步

时间:2017-05-28 13:04:04

标签: java multithreading synchronization

我刚开始使用notify,synchronized和wait,它几乎可以工作,但只有当我让第二个Thread sleep()1 ms时才会这样。你可以在最后看到我的控制台输出。

我的主要人物:

public static void main(String[] args) {

    InfoPaket paket = new InfoPaket();
    TestThread testThread = new TestThread(paket);
    TestThread2 testThread2 = new TestThread2(paket);
}

我的“锁定”类

public class InfoPaket {

String info;
char infoDataSign;
boolean newInfo = false;



public synchronized boolean isNew(){
    if (!newInfo){
        try {

            System.out.println("waiting");
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return newInfo;
}


public synchronized String getInfo() {
    newInfo = false;
    return info;
}
public synchronized void setInfo(String info) {this.info = info;
    newInfo = true;
    notify();
}

我的两个测试线程

1

public class TestThread implements Runnable {

InfoPaket info;
int i = 0;

public TestThread(InfoPaket info)  {
    this.info = info;
    new Thread(this,"TestThread").start();
}   



public void run() {
    while(true){
        i++;
        getInfo();
    }
}



 void getInfo(){
     info.isNew();
     System.out.println("he got it... " + info.getInfo() + "  " + i);
    }

2

public class TestThread2 implements Runnable{


InfoPaket info;
Thread t;
int i = 0;

public TestThread2(InfoPaket info)  {
    this.info = info;
    t = new Thread(this,"TestThread");
    t.start();
}   



public void run() {
    while(i < 500000){
        i++;
        setInfo();
    }
}



 void setInfo(){

     info.setInfo("lelaoao");
     System.out.println("here  " + i);

     try {
        t.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    }   

这里我的结果非常清晰明确(除了开头):

waiting
he got it... lelaoao  1
waiting
here  1
here  2
he got it... lelaoao  2
waiting
here  3
he got it... lelaoao  3
waiting
here  4
he got it... lelaoao  4
waiting
here  5
he got it... lelaoao  5
waiting
here  6
he got it... lelaoao  6

等等..

但是每次睡着时都会把2个线程放慢速度, 但没有它,我会发现一些我无法向自己解释的奇怪的事情:

waiting
here  1
he got it... lelaoao  1
he got it... lelaoao  2
waiting
here  2
here  3
he got it... lelaoao  3
he got it... lelaoao  4
waiting
here  4
here  5
here  6
here  7
here  8
here  9
here  10
he got it... lelaoao  5
he got it... lelaoao  6
waiting

1 个答案:

答案 0 :(得分:1)

您的代码按预期工作(如编码),除了 - 1)您有一些不良代码2)您可能误解了这个概念。

首先让我先谈谈你的代码在做什么:

  1. 您的2个线程共享InfoPaket并保存有关数据包的信息,并跟踪是否收到新信息。
  2. 然后您的TestThread将检查是否收到新信息,如果没有收到新信息,那么它将等待,一旦收到新信息,您将打印信息(始终是“lelaoao” “)以及你的循环计数器,如he got it... lelaoao 23
  3. 然后你的TestThread2将设置信息并通知等待的线程,然后像这样打印这个线程的循环计数器 - "here " + i
  4. 现在,您理解的最重要的事情是线程调度是意外的并且依赖于底层的OS线程调度机制以及JVM实现,因此您不能指望如果线程2设置了信息然后肯定线程1将执行,您可以尝试强制执行Thread.sleep(1)Thread.yeild()请注意Thread.yeild()不可移植,这对您有好处没有使用它,不应该使用它,而不是它,你应该使用Thread.sleep(1)

    现在让我们来看看糟糕的代码和一些重要的概念:

    错误代码

    1. 你做的最错误的事情是你从构造函数开始一个新线程,不要尝试从构造函数启动一个新线程,因为它会导致你的引用泄漏甚至在构造函数完成之前。阅读this excellent article
    2. 您不需要在TestThread2中引用当前线程,因为您可以直接执行Thread.sleep(1);,这将导致当前线程进入休眠状态。
    3. 您正在从主线程打印System.out.println("he got it... " + info + " " + i);System.out.println("here " + i);,但是您应该从synchronized块打印这些以确保没有交错,因为在没有同步交错时可能会发生< / strong>,您可以在he got it... lelaoao 3之前看到here 3,这在逻辑上是错误的。
    4. 现在,下面是固定代码,它将一致地产生正确的结果(考虑到你的线程1有机会在线程2设置信息后运行),输出也放在最后。< / p>

      <强> InfoPaket.java

      public class InfoPaket {
      
          String info;
          char infoDataSign;
          boolean newInfo = false;
      
          public synchronized boolean isNew() {
              if (!newInfo) {
                  try {
                      System.out.println("waiting");
                      wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              return newInfo;
          }
      
          public synchronized void getInfo(int i) {
              newInfo = false;
              System.out.println("he got it... " + info + "  " + i);
          }
      
          public synchronized void setInfo(String info, int i) {
              this.info = info;
              newInfo = true;
              System.out.println("here  " + i);
              notify();
          }
      
          public static void main(String[] args) {
      
              InfoPaket paket = new InfoPaket();
              TestThread testThread = new TestThread(paket);
              TestThread2 testThread2 = new TestThread2(paket);
      
              new Thread(testThread, "testThread").start();
              new Thread(testThread2, "testThread2").start();
          }
      }
      

      <强> TestThread.java

      public class TestThread implements Runnable {
      
          InfoPaket info;
          int i = 0;
      
          public TestThread(InfoPaket info) {
              this.info = info;
          }
      
          public void run() {
              while (true) {
                  i++;
                  getInfo(i);
              }
          }
      
          void getInfo(int i2){
               info.isNew();
               info.getInfo(i2);
          }
      }
      

      <强> TestThread2.java

      public class TestThread2 implements Runnable {
      
          InfoPaket info;
          int i = 0;
      
          public TestThread2(InfoPaket info) {
              this.info = info;
          }
      
          public void run() {
              while (i < 500000) {
                  i++;
                  setInfo(i);
              }
          }
      
          void setInfo(int i2) {
      
              info.setInfo("lelaoao", i2);
      
              try {
                  Thread.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      } 
      

      <强>输出:

      waiting
      here  1
      he got it... lelaoao  1
      waiting
      here  2
      he got it... lelaoao  2
      waiting
      here  3
      he got it... lelaoao  3
      waiting
      here  4
      he got it... lelaoao  4
      waiting
      here  5
      he got it... lelaoao  5
      waiting
      here  6
      he got it... lelaoao  6
      waiting
      here  7
      he got it... lelaoao  7
      waiting
      here  8
      he got it... lelaoao  8
      waiting
      here  9
      he got it... lelaoao  9
      waiting
      here  10
      he got it... lelaoao  10
      waiting
      here  11
      he got it... lelaoao  11
      waiting
      here  12
      he got it... lelaoao  12
      waiting
      here  13
      he got it... lelaoao  13
      waiting
      here  14
      he got it... lelaoao  14
      waiting
      here  15
      he got it... lelaoao  15
      waiting
      here  16
      he got it... lelaoao  16
      waiting
      here  17
      he got it... lelaoao  17
      waiting
      here  18
      he got it... lelaoao  18
      waiting
      here  19
      he got it... lelaoao  19
      waiting
      here  20
      he got it... lelaoao  20
      waiting
      here  21
      he got it... lelaoao  21
      waiting
      here  22
      he got it... lelaoao  22
      waiting
      here  23
      he got it... lelaoao  23
      waiting
      here  24
      he got it... lelaoao  24
      waiting
      here  25
      he got it... lelaoao  25
      waiting
      here  26
      he got it... lelaoao  26
      waiting
      here  27
      he got it... lelaoao  27
      waiting
      here  28
      he got it... lelaoao  28
      waiting
      here  29
      he got it... lelaoao  29
      waiting
      here  30
      he got it... lelaoao  30
      waiting
      here  31
      he got it... lelaoao  31
      

      要明确的重要概念

      我认为你可能会错过一些概念,所以你认为输出是疯狂的。

      1. 一旦使用线程2设置了信息,就不能保证线程1会运行,因为线程调度是意外的,并且依赖于底层的OS线程调度机制以及JVM实现,我在开始时突出了更多关于这一点的观点使用Thread.yield()Thread.sleep()。有了这个说明,如果你不使用Thread.sleep(1)那么你就不能指望输出是一致的,正如我在下面所示。
      2. 执行Thread.sleep时,如果该线程正在获取锁定,则不会释放锁定。
      3. 一旦你执行notify();,你就不能指望等待线程立即“可运行”,并且获取锁定的线程将不会立即释放锁定notify();是调用后,只有在同步块/方法完成后才会释放锁。
      4. 如果你在第二个帖子中没有Thread.sleep(1),那么你就不能指望一致的输出,以及我上面解释的原因。
      5. OP的评论:

          

        您是否知道从中传输数据的更有效方法?   线程到另一个?

        您有“共享内存”或“消息传递”;共享内存就是我们在这种情况下正在做的事情,如果你想进行消息传递,那么你可以使用像阻塞队列(https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html)实现这样的Java API,但这就变成了不同的故事。