初学java,菜鸟笔记。
synchronized关键字用于java编程中的线程同步。在详细讲之前,我想闲谈谈我对java同步的理解。对于每个类来说,java有两条设定好的独一无二的同步路线,两把锁:一个是类对象的同步也就是对象锁(暂称为锁A),一个是类的同步也就是类锁(暂称为锁B)。什么意思呢,首先说类对象的同步,也就是对象锁。类对象的同步也就是说每个类对象都有一条同步路线,好比一辆车,任何添加了synchronized关键字的非static方法或者synchronized(this){}块都只能上这辆车,车上同时只能有一个人。注意,每个类对象都有一辆单独的车,对象之间不影响。这辆车就是对象的锁,属于一个类对象!任何被synchronized关键字修饰的非static方法都默认尝试占有这把锁A,另外,synchronized(this){}代码块也尝试占有这把锁A,因为参数是this。以上这两种占有锁的方法,都试图占有同一把锁A,因此它们是同步的。因此,下面的两种形式实际上是等价的:
public void function(){
synchronized(this){}
}
和
public synchronized void run(){
}
因为二者都尝试占有对象锁A。下面用一个简单的代码解释一下这个问题。
package ttt; public class testtt9 implements Runnable { public int num=0; public synchronized void run(){ num=1; try{ Thread.sleep(3000); System.out.println("th1 finished"); }catch(InterruptedException e){ e.printStackTrace(); } } // public void show(){ // synchronized(new Object()){//这个同步代码块是尝试对新new的对象进行加锁(无所谓到底是什么对象),而上面的run是对当前对象加锁,在run上加锁 //不是一个好习惯,因为这意味着线程整个执行期间都占有了锁,线程中其他同步方法将没有任何执行时间,但此处 //没有任何影响,因为后面分别在主线程中访问num以及在对其他对象进行占有的同步块中访问num。 // System.out.println("num="+num); // } // } public void show(){ synchronized(this){ System.out.println("num="+num); } } public static void main(String[] args) throws Exception { testtt9 t9=new testtt9(); Thread th1=new Thread(t9); th1.start(); Thread.sleep(1); t9.show(); } }
上面代码的输出结果是:
显然线程th1结束后才完成了访问,run()和show()是同步的。但如果把代码中的synchronized(this){}改为synchronized(new Object()){},其输出结果变为:
显然线程th1和show()方法不同步了,原因是虽然二者都尝试占有对象锁,但是run()尝试占有this的锁,也就是当前对象的锁,而show方法占有其他一个新new对象的锁,因此二者不冲突,不会同步。
然后再将第二个同步,类的同步,这个同步也只有一把锁B,想申请这把锁可以使用synchronized static public void fun(){};的形式,即对静态方法添加synchronized关键字,也可以使用synchronized代码块的语法,形式为public void fun(){synchronized(this.class)}; 也就是说下面两条语句的功能是一样的。
synchronized public static void show(){ }
和
public static void show(){
synchronized(this.getClass){}
下面看一个简单的小例子。
package ttt; import javax.swing.text.html.HTMLDocument.Iterator; public class testtt9 implements Runnable { public static int num1; public void run(){ synchronized (this.getClass()) { num1++; try{ Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } } } static synchronized public void fun1() throws Exception{ num1++; Thread.sleep(3000); } public static void showNum(){ System.out.println(num1); } public static void main(String[] args) throws Exception{ testtt9 t9=new testtt9(); Thread th1=new Thread(t9); th1.start(); Thread.sleep(1); testtt9.fun1(); Thread.sleep(1); testtt9.showNum(); } }
这个例子中分别使用两种不同的方法去占有类testtt9的类锁,最终的目标是使静态变量两次增加1为2。而且增加后会休眠3秒钟,令二者近乎同时进行,最后发现程序执行6秒钟后显示为2。这说明二者是同步的。另外,为何语句testtt9.showNum();在6秒钟之后才执行? 看起来似乎main方法也参与了同步的过程。非也。这是因为虽然run()方法和fun1()方法同时执行,但实际fun1()一直在等待占有锁,程序并没有往下执行。