有人能告诉我同步方法比同步块的优势与一个例子吗?


当前回答

Synchronizing with threads. 1) NEVER use synchronized(this) in a thread it doesn't work. Synchronizing with (this) uses the current thread as the locking thread object. Since each thread is independent of other threads, there is NO coordination of synchronization. 2) Tests of code show that in Java 1.6 on a Mac the method synchronization does not work. 3) synchronized(lockObj) where lockObj is a common shared object of all threads synchronizing on it will work. 4) ReenterantLock.lock() and .unlock() work. See Java tutorials for this.

The following code shows these points. It also contains the thread-safe Vector which would be substituted for the ArrayList, to show that many threads adding to a Vector do not lose any information, while the same with an ArrayList can lose information. 0) Current code shows loss of information due to race conditions A) Comment the current labeled A line, and uncomment the A line above it, then run, method loses data but it shouldn't. B) Reverse step A, uncomment B and // end block }. Then run to see results no loss of data C) Comment out B, uncomment C. Run, see synchronizing on (this) loses data, as expected. Don't have time to complete all the variations, hope this helps. If synchronizing on (this), or the method synchronization works, please state what version of Java and OS you tested. Thank you.

import java.util.*;

/** RaceCondition - Shows that when multiple threads compete for resources 
     thread one may grab the resource expecting to update a particular 
     area but is removed from the CPU before finishing.  Thread one still 
     points to that resource.  Then thread two grabs that resource and 
     completes the update.  Then thread one gets to complete the update, 
     which over writes thread two's work.
     DEMO:  1) Run as is - see missing counts from race condition, Run severa times, values change  
            2) Uncomment "synchronized(countLock){ }" - see counts work
            Synchronized creates a lock on that block of code, no other threads can 
            execute code within a block that another thread has a lock.
        3) Comment ArrayList, unComment Vector - See no loss in collection
            Vectors work like ArrayList, but Vectors are "Thread Safe"
         May use this code as long as attribution to the author remains intact.
     /mf
*/ 

public class RaceCondition {
    private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)

    private String countLock="lock";    // Object use for locking the raceCount
    private int raceCount = 0;        // simple add 1 to this counter
    private int MAX = 10000;        // Do this 10,000 times
    private int NUM_THREADS = 100;    // Create 100 threads

    public static void main(String [] args) {
    new RaceCondition();
    }

    public RaceCondition() {
    ArrayList<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class

其他回答

谁能告诉我同步方法比同步块的优势与一个例子?谢谢。

与块相比,使用同步方法并没有明显的优势。

也许唯一的一点(但我不认为这是优点)是您不需要包含对象引用this。

方法:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

布洛克:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

看到了吗?一点好处都没有。

块确实比方法有优势,主要是灵活性,因为你可以使用另一个对象作为锁,而同步方法将锁定整个对象。

比较:

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

vs.

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

另外,如果方法增长了,你仍然可以保持同步段的分离:

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}

唯一的区别是:同步块允许颗粒状锁定,不像同步方法

基本上同步块或方法被用来编写线程安全的代码,以避免内存不一致的错误。

这个问题很老了,在过去的7年里,很多事情都发生了变化。 为了线程安全,引入了新的编程结构。

您可以通过使用高级并发API而不是同步块来实现线程安全。该文档页提供了实现线程安全的良好编程结构。

锁对象支持简化许多并发应用程序的锁定习惯用法。

executor为启动和管理线程定义了高级API。concurrent提供的执行器实现提供了适合大型应用程序的线程池管理。

并发集合使管理大型数据集合变得更容易,并且可以大大减少同步的需要。

原子变量具有最小化同步和帮助避免内存一致性错误的特性。

ThreadLocalRandom(在JDK 7中)提供了从多个线程有效生成伪随机数的功能。

更好的synchronized替代品是ReentrantLock,它使用Lock API

一个可重入互斥锁,其基本行为和语义与使用同步方法和语句访问的隐式监视锁相同,但具有扩展功能。

锁的例子:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

其他编程结构也可以参考java.util.concurrent和java.util.concurrent.atomic包。

参考这个相关的问题:

同步vs锁定

在同步方法的情况下,锁将在对象上获得。但是如果你使用同步块,你可以选择指定一个对象来获取锁。

例子:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }

通常在方法级别上使用锁是不礼貌的。为什么要通过锁定整个方法来锁定一段不能访问任何共享资源的代码呢?因为每个对象都有一个锁,所以可以创建虚拟对象来实现块级同步。 块级的效率更高,因为它不锁定整个方法。

这里有一些例子

方法级

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

块级别

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

(编辑)

对于像Vector和Hashtable这样的集合,当ArrayList或HashMap不同步时,它们是同步的,你需要设置synchronized关键字或调用Collections synchronized方法:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list

在实际应用中,同步方法相对于同步块的优势在于它们更能抵抗白痴;因为您不能选择任意对象来锁定,所以您不能滥用synchronized方法语法来做一些愚蠢的事情,比如锁定字符串文字或锁定从线程下面更改的可变字段的内容。

另一方面,使用同步方法,您无法保护锁不被任何可以获得对象引用的线程获取。

因此,在方法上使用synchronized作为修饰符可以更好地保护你的奶牛免受伤害,而将synchronized块与私有final锁对象结合使用则可以更好地保护你自己的代码免受奶牛的伤害。