同步块上的Java线程锁定

时间:2011-04-13 04:25:08

标签: java multithreading

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

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);
    }
}

这里在上面的run方法中我在同步块中给出了这个...我想要它们之间的区别

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

3 个答案:

答案 0 :(得分:4)

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

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

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

关于第二个代码,相当于

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虽然问题本身是关于静态方法,但它也适用于实例成员方法。

答案 1 :(得分:3)

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

a)第一个例子没问题,因为synchronized关键字正在守护StringBuffer线程共享一个锁。

b)第二个例子不行。你给每个线程一个不同的锁。实际上,它没有任何影响(事实上,现代Java编译器完全删除了这些锁)。如果多个线程使用锁,则锁是有意义的。

你可以这样想:
如果您共用一间浴室,最好有一把锁(如门钥匙)。如果你要求每个人在使用卫生间之前锁定他们自己的个人iPhone,那肯定是无用的。请注意,共享外观不一定是门钥匙。您可以选择一个iPhone并将其用作浴室“钥匙”(每个人都必须在使用浴室之前锁定那个 iPhone,并且只有锁定它的人才可以解锁它)。在现实生活中听起来很荒谬,但这几乎就是我们用互斥体做的事情。

c)第二个例子可以被认为是错误的,但在实践中,你不会看到竞争条件的影响。这是因为StringBuffer在内部同步。如果您改为使用StringBuilder可能能够看到竞争条件(取决于运行时条件)。

答案 2 :(得分:2)

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

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