synchronized块(方法)的探索

先介绍synchronized方法和synchronized块的使用,分割线后边是不断的修改一个例子,去探索synchronized的作用
一、synchronized方法
当一个对象所有的方法全部被synchronized关键字标识以后,当一个线程A访问一个带synchronized关键字的方法时(比如method1),其他的方法就会被阻塞(不允许其他现场访问这个类的方法),得等A线程访问任务完成以后,才能让其他线程访问。但是要达到这样,必须是所有方法全部标记synchronized关键字。

public class Try{
  public synchronized void method1() {  
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
  }
  public synchronized void method2() {
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
  }
  public static void main(String[] args) {  
    final Try tt = new Try();  
    Thread A = new Thread(new Runnable() {  public void run() { tt.method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {tt.method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

输出结果为:

B : 0
B : 1
B : 2
B : 3
A : 0
A : 1
A : 2
A : 3

二、synchronized块
顾名思义,就是类似于 synchronized(Object){}这样的块,功能跟synchronized方法是一样的。代码必须获得对象Object的锁才能执行。
例:

public class Try{
  public void method1() {  
    synchronized(this) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public void method2() {
    synchronized(this) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public static void main(String[] args) {  
    final Try tt = new Try();  
    Thread A = new Thread(new Runnable() {  public void run() { tt.method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {tt.method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

输出结果为:

B : 0
B : 1
B : 2
B : 3
A : 0
A : 1
A : 2
A : 3

下面是通过几个例子对synchronized的探索:
理解1:两个并发线程访问同一个对象object的同步代码块时,我们通过synchronized的手段,使得只有一个线程在执行任务
例1:

public class Try{
  public void method1() {  
   synchronized(this) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public  void method2() {
   synchronized(this) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public static void main(String[] args) {  
    Thread A = new Thread(new Runnable() {  public void run() { new Try().method1();  }  }, "A"  ); 
    Thread B = new Thread(new Runnable() {  public void run() {new Try().method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

很简单的例子,创建两个线程,名字分别为A和B,希望他们能不是并发的执行任务,
输出结果却是:

B : 0
A : 0
B : 1
A : 1
B : 2
A : 2
B : 3
A : 3

解释:显然,创建的A、B两个线程是同步进行的,A线程执行的时候并不会阻塞B线程,可是明明已经使用了synchronized了啊?原因在于:synchronized解决的是对类的对象实例进行加锁,当线程调用一个实例运行的,另外的线程调用这个实例时候阻塞,达到上锁的目的,但是这里明显创建的是两个对象new Try,创建了两个对象实例。(即A访问的是一个实例,B访问是另一个)(对比理解3)
应该把main方法里边的创建方式改成:同一个对象监控器,即只new一个。(还有一个解决方式,见理解4)

final Try tt = new Try();  
    Thread A = new Thread(new Runnable() {  public void run() { tt.method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {tt.method2();   }  }, "B"  ); 
    A.start();
    B.start();

结果为:

B : 0
B : 1
B : 2
B : 3
A : 0
A : 1
A : 2
A : 3

理解2:前面说到,该对象的所有方法都需要添加synchronized,为了确保访问一个方法的时候,对其他线程的阻塞,如果不呢?
把上边例子的method2去掉了synchronized,变成

public  void method2() { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
}

(注:main方法采用上边那个修改过的方法)
输出结果是:

B : 0
B : 1
A : 0
B : 2
A : 1
B : 3
A : 2
A : 3

解释:很明显,创建两个进程,但是method2没有synchronized关键字(即method1对他没有阻塞作用),所以method2可以跟method1一起执行,即:对其它不是synchronized同步方法或不是synchronized(this)同步代码块调用不是堵塞状态的

理解3:使用synchronized(非this对象)同步代码块格式进行同步操作时,对象监视器必须是同一个对象,如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行
例子3:

public class Try{
  public void  method1() {  
    String lock = new String(); 
    synchronized(lock) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public void method2() {
   String lock = new String(); 
   synchronized(lock) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public static void main(String[] args) {  
    final Try tt = new Try();  
    Thread A = new Thread(new Runnable() {  public void run() { tt.method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {tt.method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

可以看到对象监视是lock,但是分别是两个new,他们是不一样的,所以结果会交叉:

A : 0
B : 0
A : 1
B : 1
A : 2
B : 2
A : 3
B : 3

应该改成:变成同一个对象监视器

public class Try{
  private String lock = new String(); 
  public void  method1() {   
    synchronized(lock) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public void method2() {
   synchronized(lock) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public static void main(String[] args) {  
    final Try tt = new Try();  
    Thread A = new Thread(new Runnable() {  public void run() { tt.method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {tt.method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

理解4:使用静态同步synchronized方法和*.Class方式持锁
理解1的例子,也可以这样修改:

public class Try{
  public void  method1() {   
    synchronized(Try.class) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public void method2() {
   synchronized(Try.class) { 
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
    }
  }
  public static void main(String[] args) {    
    Thread A = new Thread(new Runnable() {  public void run() { new Try().method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {new Try().method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

把synchronized里的对象监视器变成相同的(不用this了,因为创建线程的时候,实例对象,this所指的,是两个对象)

或者静态同步synchronized方法

public class Try{
  public  synchronized static void  method1() {   
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
  }
  public synchronized static void method2() {
      for(int i=0;i<4;i++) {
        System.out.println(Thread.currentThread().getName()+" : "+i);
      }
  }
  public static void main(String[] args) {    
    Thread A = new Thread(new Runnable() {  public void run() { method1();  }  }, "A"  ); 
    Thread B = new Thread(  new Runnable() {  public void run() {method2();   }  }, "B"  ); 
    A.start();
    B.start();
  }
}

注意:变成静态方法以后,就不用new Try().method1 直接用method1就可以。
解释:为什么这样的方法可以呢?因为静态方法与类相关,而不是对象,线程获取的是Class锁。

猜你喜欢

转载自blog.csdn.net/leafdown_/article/details/80682187