java synchronized关键字解惑

时间:2023-01-31 17:42:27

        初学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();
	}
}

    上面代码的输出结果是:

java synchronized关键字解惑   显然线程th1结束后才完成了访问,run()和show()是同步的。但如果把代码中的synchronized(this){}改为synchronized(new Object()){},其输出结果变为:

java synchronized关键字解惑

    显然线程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()一直在等待占有锁,程序并没有往下执行。