2017-04-12 34 views
2

目的:创建4个线程,在每个线程中for循环将1个共享int变量添加1万次。最后,共享变量预计为40000.我使用两种方式来做到这一点,每次我运行代码时,结果并不总是40000.我不知道什么是错的。使用java多线程计数,似乎不工作

方法1同步:

public class bbb {  
    int i=0; 
    public void add() { 
     synchronized(bbb.class){  
      i++; 
      bbb.class.notifyAll(); 
      System.out.println(i); 
      try { 
       bbb.class.wait(); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
    public static void main(String[] args) { 
     Thread t1=new Thread(new Runnable() { 
      bbb b=new bbb(); 
      @Override 
      public void run() { 
       // TODO Auto-generated method stub 
       int i=0; 
       for (; i < 10000; i++) { 
        b.add(); 
       } 
      } 
     },"A"); 

     Thread t2=new Thread(t1,"B"); 
     Thread t3=new Thread(t1,"C"); 
     Thread t4=new Thread(t1,"D"); 
     t1.start(); 
     t2.start(); 
     t3.start(); 
     t4.start(); 
    } 
} 

方法2乐观锁:

static volatile int value=0; 
static void optimisticLock(int expected,int update){ 
    if(expected==value){  
     value=update; 
     System.out.println(Thread.currentThread().getName()+" "+update); 
    }else{ 
     optimisticLock(value, value+1); 
    } 
} 

public static void main(String[] args) { 

    Thread t1=new Thread(new Runnable() { 

     @Override 
     public void run() { 
      // TODO Auto-generated method stub 
      for(int i=0;i<10000;i++){ 
       optimisticLock(value, value+1); 
      } 
     } 
    },"A"); 
    Thread t2=new Thread(t1,"B"); 
    Thread t3=new Thread(t1,"C"); 
    Thread t4=new Thread(t1,"D"); 
    t1.start(); 
    t2.start(); 
    t3.start(); 
    t4.start(); 
} 
+0

结果是39996? –

+2

为什么你需要在那里等待和通知?不明白 – developer

+0

我运行了程序,当java进程达到39950+数时卡住java进程,可能存在某种死锁。不知道你要怎么处理这么多的并发症。这可以做得更简单一点。 – Zeus

回答

1

你实际上并不需要wait()notify()存在,这是造成这个问题,好像你是困惑。在一般情况下,wait()notify()在生产者/消费者问题使用(请参阅here),并在你的代码,你只是想控制访问来自各个线程的int变量,你不需要waitnotify

您可以参考下面的代码,它符合您的目标与上面的Method1(运行4个线程并使用同步打印值),我已经基本上取出wait()notify()调用。

public void add() { 
    synchronized(Testing.class){  
      i++; 
      System.out.println(i); 
    } 
} 

而且,在你的代码,这使得使用synchronized(this),因为你是在实例变量i(从bbb对象)同步更有意义。


您可能需要注意的是,您的任务(使用跨线程int变量)另外一点,你可以简单地使用AtomicInteger API,使用它可以避免在代码中显式同步


另外,我建议你重构,如下图所示,因为传递thread1对象thread2你的代码是非常的混乱,很难理解(此外,还要确保Java的命名约定,类名称以大写字母开头):

public class BBBThread implements Runnable { 

    int i=0; 
    public void add() { 
     synchronized(this){  
      i++; 
      System.out.println(i); 
     } 
    } 

    @ooverride 
    public void run() { 
     for (int i=0; i < 10000; i++) { 
      add(); 
     } 
    } 

    public static void main(String[] args) { 
     BBBThread bbbObj =new BBBThread(); 
     Thread t1=new Thread(bbbObj,"A"); 
     Thread t2=new Thread(bbbObj, "B"); 
     Thread t3=new Thread(bbbObj, "C"); 
     Thread t4=new Thread(bbbObj, "D"); 
     t1.start(); 
     t2.start(); 
     t3.start(); 
     t4.start(); 
    } 
} 
+1

为什么不使用AtomicNumber? –

+0

只是在你评论时加上:-) – developer

+0

我写了你在第一次发布的代码,它工作。然后我学会了等待,通知机制,并将其添加到我的代码中。我写它给我学习java,所以代码可能看起来很混乱,因为代码不是有时要完成的。 – abracadabra

0

[[@ @ javaguy的答案是现货,但有一些东西丢失了,所以我想我会为后代做这件事。 ]]

首先,您应该使用AtomicInteger,它可以将所有的手动同步带走。你的线程只需要做:

AtomicInteger i = new AtomicInteger(); 
... 
for (int i = 0; i < 10000; i++) { 
    i.incrementAndGet(); 
} 

没有必要的同步,因为AtomicInteger包装了volatile int领域,具有逻辑妥善处理并发更新。

我使用了两种方法来做到这一点,每次运行代码时结果并不总是40000.i不知道有什么问题。

你永远不会达到40000的原因是你有你的手同步周围的竞争条件。在4个线程中的3个线程完成10000之后,最后一个线程将调用wait(),但没有人通知它。如果你只是删除你的等待,并通知你的代码总是对我的作品:

synchronized(bbb.class) { 
     // no wait/notify here 
     i++; 
     System.out.println(i); 
} 

夫妇的其他意见:

  • 在这样一类绝对不要锁住。这意味着其他线程可以调用通知并在该类上等待并影响其内部操作。如果你需要一个static锁然后创建一个static锁定对象: private static final Object lockObject = new Object();
  • 要小心,在多线程程序做System.out.println(...)因为System.out是同步本身PrintStream。更好的模式是从​​内拍摄i的快照,然后在外部打印。
  • 一般试图做由于性能原因,在​​块内的IO。
  • 类应该以大写字母开头,所以它应该是BbbThreadedCounter
  • 正如@javaguy提到的,您正在将Thread传递给另一个线程的构造函数。这是有效的,因为ThreadRunnable,但它是一个非常糟糕的模式,非常混乱。你应该这样做:

    Runnable runnable = new Runnable() { ... }; 
    Thread t1 = new Thread(runnable, "A"); 
    Thread t2 = new Thread(runnable, "B"); 
    ... 
    
  • 这将是更好地创建类的实例,并把它传递给每个然后可以在它的instance锁的线程:

    synchronized (this) { 
        i++; 
    } 
    ... 
    final Bbb bbb = new Bbb(); 
    Thread t1 = new Thread(new Runnable() { 
        @Override 
        public void run() { 
         for(int i = 0; i < 10000; i++){ 
          bbb.add(); 
         } 
        } 
    }, "A"); 
    

但是,该包中的AtomicInteger和其他并发类是您的朋友。