java中中synchronized的用法详解

阅读更多
1.对象锁:
  1.1对象锁是run方法所在类的实例
  synchronized 修饰非静态方法,或者  synchronized(this)时
   public synchronized void method() { // todo }与
   public void method(){synchronized(this) {// todo} }等价
  1.1.1 结论:
  当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
1.1.1示例说明:
  public class ThreadTest implements  Runnable{

public  void run() {
method1();
}

private synchronized void method1(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj1,"线程2");
t1.start();
t2.start();
}
}
1.1.1执行结果:
线程1:  0
线程1:  1
线程1:  2
线程1:  3
线程1:  4
线程2:  0
线程2:  1
线程2:  2
线程2:  3
线程2:  4
  1.1.2结论:当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块.
  1.1.2示例说明:
  public class ThreadTest implements  Runnable{

public  void run() {
if("线程1".equals(Thread.currentThread().getName())){
method1();
}else{
method2();
}

}

private synchronized void method1(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private  void method2(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj1,"线程2");
t1.start();
t2.start();
}
}
1.1.2执行结果:
线程1:  0
线程2:  0
线程1:  1
线程2:  1
线程2:  2
线程1:  2
线程1:  3
线程2:  3
线程1:  4
线程2:  4
1.1.3结论:
  当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞
1.1.3示例代码:
  更改1.1.2的method2方法,
  在method2上添加synchronized
  private  synchronized void method2(){
  //....
         }
  或者
   private   void method2(){synchronized(this){
    //..
  }}
1.1.3 执行结果
线程1:  0
线程1:  1
线程1:  2
线程1:  3
线程1:  4
线程2:  0
线程2:  1
线程2:  2
线程2:  3
线程2:  4
1.1.4结论:
  多个线程访问不同的对象时,不会存在互斥的情况
  1.1.4 示例代码:
      将1.1.4 示例代码的main改为如下:(每个线程访问不同的对象)
      public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
ThreadTest obj2= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj2,"线程2");
t1.start();
t2.start();
}
  1.1.4 执行结果
线程1:  0
线程2:  0
线程2:  1
线程1:  1
线程1:  2
线程2:  2
线程1:  3
线程2:  3
线程1:  4
线程2:  4

1.2 对象锁不是run方法所在类的实例
 
  1.2.1 指定要给某个对象加锁

  1.2.1 示例代码
  class Account {
   String name;
   float amount;

   public Account(String name, float amount) {
      this.name = name;
      this.amount = amount;
   }
   //存钱
   public  void deposit(float amt) {
      amount += amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   //取钱
   public  void withdraw(float amt) {
      amount -= amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   public float getBalance() {
      return amount;
   }
}
      class AccountOperator implements Runnable{
   private Account account;
   public AccountOperator(Account account) {
      this.account = account;
   }

   public void run() {
      synchronized (account) {
         account.deposit(500);
         account.withdraw(500);
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
      }
   }
  
   public static void main(String[] args) {
   Account account = new Account("zhang san", 10000.0f);
   AccountOperator accountOperator = new AccountOperator(account);

   final int THREAD_NUM = 5;
   Thread threads[] = new Thread[THREAD_NUM];
   for (int i = 0; i < THREAD_NUM; i ++) {
      threads[i] = new Thread(accountOperator, "Thread" + i);
      threads[i].start();
   }
}
}

1.2.1 执行结果:
Thread0:10000.0
Thread4:10000.0
Thread3:10000.0
Thread2:10000.0
Thread1:10000.0

说明:account作为锁对象,起到了同步的效果

1.2.2 只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:
  创建 byte[] lock= new byte[0];比创建Object开销小,所以推荐byte[0]。
示例代码:
public class ThreadTest implements  Runnable{
byte[] lock= new byte[0];
public  void run() {
method2();
}



private    void method2(){
synchronized (lock) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj1,"线程2");
t1.start();
t2.start();
}
}

执行结果:
线程1:  0
线程1:  1
线程1:  2
线程1:  3
线程1:  4
线程2:  0
线程2:  1
线程2:  2
线程2:  3
线程2:  4

说明:这种方式结果看似与private synchronized    void method2(){//..}一致,但是synchronized (lock) 的锁是lock,而private synchronized    void method2()锁是this。

1.2.3  静态的成员作为锁:
将示例1.2.2中byte[] lock= new byte[0]; 改为static byte[] lock= new byte[0];
这样,lock就只有一份,任何时候,最多一个线程进入到lock锁定的执行体
 
1.2.4 常量作为锁
  public class ThreadTest2 implements  Runnable{
public  void run() {
method1();
}

private    void method1(){
synchronized ("123") {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}
}

  public class ThreadTest implements  Runnable{

public  void run() {
method1();

}

private    void method1(){
synchronized ("123") {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}



public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
ThreadTest2 obj2= new  ThreadTest2();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj2,"线程2");
t1.start();
t2.start();
}
}
说明:常量作为锁,所有的用到这个常量的同步,都只能有一个线程能执行,不推荐

2.类锁:
synchronized 修饰静态方法 或者    synchronized(类名.class) {  // todo}
    public   static synchronized void methodB() {
    }
    与
    public  void methodB() {  
       synchronized(所在类.class){
       }
    }
   在多线程层面上来看,是等价的


示例2.1:
public class ThreadTest implements  Runnable{

public  void run() {
if("线程1".equals(Thread.currentThread().getName())){
method1();
}else{
method2();
}

}

private synchronized static  void method1(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private synchronized static  void method2(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
ThreadTest obj2= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj2,"线程2");
t1.start();
t2.start();
}
}

执行结果:
线程1:  0
线程1:  1
线程1:  2
线程1:  3
线程1:  4
线程2:  0
线程2:  1
线程2:  2
线程2:  3
线程2:  4

解释说明:
这里的锁是ThreadTest 类,所有对象共用这一把锁。

示例2.2
public class ThreadTest implements  Runnable{

public  void run() {
if("线程1".equals(Thread.currentThread().getName())){
method1();
}else{
method2();
}

}

private synchronized static  void method1(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private  synchronized  void method2(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":  "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
ThreadTest obj1= new  ThreadTest();
Thread  t1= new Thread(obj1,"线程1");
Thread t2= new  Thread(obj1,"线程2");
t1.start();
t2.start();
}
}

执行结果:
线程1:  0
线程2:  0
线程2:  1
线程1:  1
线程2:  2
线程1:  2
线程1:  3
线程2:  3
线程1:  4
线程2:  4


解释说明:
因为method1的锁是ThreadTest 类,而method2的锁是当前对象(也就是这里的obj1),锁不同,不会互斥。

线程通俗描述:
一个object就像一个大房子,大门永远打开。房子里有 很多房间(也就是方法)。
这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房子外放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。所有想调用该对象方法的线程比喻成想进入这房子某个房间的人。第一个人进入房间后,房子外来了2个人,这两个人要等,第一个人从房间内出来,即使他还想去其他房间,也要把钥匙还回来,重新竞争。

你可能感兴趣的:(java中中synchronized的用法详解)