2011-04-13 98 views
0

我正在学习带有锁的同步块。我想知道这个锁和程序中提供的某个第三方锁之间的区别。同步块上的Java线程锁

public class NewThread extends Thread { 
StringBuffer sb; 
NewThread(StringBuffer sb){ 
    this.sb=sb; 
} 
public void run(){ 
    synchronized(this.sb){ 
     for(int i=0;i<1000;i++){ 
      System.out.print(sb); 
       try{ 
      Thread.sleep(5*60); 
     } 
     catch(Exception e){} 
     } 
     char c = this.sb.charAt(0); 
     this.sb.setCharAt(0, ++c); 
    } 
} 
public static void main(String[] args){ 
    StringBuffer sb=new StringBuffer("A"); 
    NewThread nt=new NewThread(sb); 
    NewThread nt1=new NewThread(sb); 
    NewThread nt2=new NewThread(sb); 
    nt.start(); 
    nt1.start(); 
    nt2.start(); 
} 

}

如果我打算把

public void run(){ 
    synchronized(this){ 
     for(int i=0;i<1000;i++){ 
      System.out.print(sb); 
       try{ 
      Thread.sleep(5*60); 
     } 
     catch(Exception e){} 
     } 
     char c = this.sb.charAt(0); 
     this.sb.setCharAt(0, ++c); 
    } 
} 

这里在上面运行方法我在synchronized块给这个......我希望他们

之间的区别

我还有一个问题,如果我们在同步块中给出锁对象,并且我们没有在该块内使用该对象,那么我们是否会观察到与正常块相比的任何特定事物

回答

4

如果您具有一般OS API的并发开发经验,例如在Linux中的pthread库中,您可能已经知道我们应该使用锁定或其他数据结构来同步访问关键部分(共享对象可能会被修改)的进程/线程。

Java使用锁来实现同步块。同步块是一种机制(称为操作系统中的监视器),封装了与锁有关的繁琐操作。每个对象在java中都有一个锁。在同步时,首先锁定共享对象(也可以说其他进程需要共享对象的锁定)。如果某个线程无法获得该锁,那意味着其他某个线程现在持有锁,它必须等到其他线程释放该锁并再次重新获取,直到它锁定该锁并进入关键部分。

第一个代码片段在StringBuffer实例中使用lock,即sb,每个线程都会尝试获取sb的锁(在运行代码之前调用lock_of_sb.lock(),只有成功获取sb的锁才能最终执行代码。

至于第二个码,这相当于

public synchronized void run(){ 
    for(int i=0;i<1000;i++){ 
     System.out.print(sb); 
     try{ 
     Thread.sleep(5*60); 
    } 
    catch(Exception e){} 
    } 
    char c = this.sb.charAt(0); 
    this.sb.setCharAt(0, ++c); 
} 

我不认为它的行为如你预期它在这个对象获取锁,但是,此对象永远不会共享,所以共享sb在没有任何同步的情况下暴露在关键部分中

该链接将为您提供另一种实现同步的工作方式。 How to synchronize static method in java虽然问题本身是关于静态方法的,但它也适用于实例成员方法。

+0

谢谢我的理解..但我没有得到共享锁或共享锁..可以解释一下 – satheesh 2011-04-13 05:01:41

+0

@satheesh我不知道这是你想要的:在一个同步的多线程/进程系统中,通常只有一种资源可以存在一个锁。它用于控制共享对象。在Linux中,锁和共享对象是分开的。在Java中,每个对象都有自己的锁(可以在类Object的源代码中找到)。也许在linux中,我们可以说一个锁被一些进程共享,因为锁和进程/线程之间没有关系。但在Java中,由于每个对象都有自己的锁,因此不需要明确地共享锁。该锁由对象隐式维护。随意问。 – 2011-04-13 05:15:15

+0

确定我明白..但是每一个你与Linux有关的事情,我有点困惑 – satheesh 2011-04-13 05:40:39

2

在Java中,每个对象都可以用作互斥锁(请参阅Mutual Exclusion)。这意味着在任何时候只有一件事物可以同步在一个对象上,你使用的对象通常是不敬的,尽管它应该尽可能具体。例如,如果多个线程正在访问List,则应该在该列表上而不是在整个对象上同步(this),以便其他需要对象中的其他内容可以访问它的东西。

我认为关于互斥的文章可能有助于澄清此事。实质上,只有一个线程可能会获得“锁定”的密钥 - 该锁定是同步内部的。只要访问您的资源的所有内容都请求SAME对象上的锁,您就会受到保护。

+0

好的解释! – slezica 2011-04-13 04:33:08

+0

如果你不知道我可以解释有关我给出的例子...... – satheesh 2011-04-13 04:33:35

+0

Satheesh,你的例子仅在它们锁定的对象上有所不同。在第一个示例(this.sb)中 - 所有这一切意味着一次只有一个线程可以位于同步(this.sb)块内。你的第二个例子意味着只有一个线程可以位于synchronized(this){}块内。使用哪一个取决于 - 你想要阻止线程同时访问ENTIRE对象,或者只是字符串缓冲区? – Ben 2011-04-13 04:46:02

3

其他人已经回答了,但增加我的2美分,

一)第一个例子是确定的,在这个意义上,​​关键字守着StringBuffer线程共享一个锁。

b)第二实施例是不正常。你给每个线程一个不同的锁。实际上,它没有任何效果(事实上,现代Java编译器完全移除了这种锁)。如果多个线程使用它,则锁定是有意义的。

你可以这样想:
如果你共用一间浴室,你最好有一把锁的浴室(如门钥匙)。如果您在使用浴室之前要求所有人锁定他们自己的个人iPhone,那肯定没用。请注意,共享外观不需要成为门钥匙。你可能会以及挑选一个iPhone,并把它作为浴室的“钥匙”(每个人都有使用的厕所,只有谁锁定它可以解锁的家伙之前锁定 iPhone)。在现实生活中,这听起来很荒谬,但这与我们用互斥体做的很相似。

c)第二个例子可以被认为是越野车,但实际上,你不会看到竞争条件的影响。这是因为StringBuffer内部同步。如果您使用StringBuilder相反,你可能能够看到竞态条件(取决于运行条件)。

+0

+1指出StringBuffer是线程安全的。 – 2011-04-13 05:48:29