java synchronized关键字

  

Javasynchronized关键字和对象的内置锁结合使用,用来保护代码块在并发环境下的线程安全,可以使被保护的代码块操作原子性。

synchronized关键字可以用于修饰方法来保护方法内的全部代码块,可以用synchronized(对象1) 的方式保护指定代码块。(这里说一下:很多书中都说synchronized可以给对象加锁,我实在不愿意这么说,这样让我概念混淆。。。因为,对象内置锁是本来就存在的,不是谁加给它的,“synchronized(对象1)”我更愿意解释成:执行到此的线程需要去获取到(持有)“对象1”的对象锁才能执行synchronized块中的代码)。

而多个线程在执行某一被synchronized保护的代码或者方法时,能够进行互斥的关键在于synchronized的是不是同一个锁。下面是synchronized使用的几种情况并分析他分别持有的是哪一个对象锁(假设这些方法都属于 Salary):

1、  synchronized修饰非静态方法:持有Salary类实例的对象锁

2、  synchronized修饰静态方法:持有Salary.class的对象锁

3、  synchronized(对象1):持有对象1 的对象锁

4、  synchronized(this):持有Salary类当前对象实例的对象锁

5、  synchronized(Salary.class): 持有Salary.class的对象锁

下面用几个程序实验一下。

一、synchronized修饰非静态方法:持有Salary类实例的对象锁

并发对象类Salary:

public class Salary {
	public synchronized void method1(){
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
	}
	
	public void  method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

并发程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Salary salary = new Salary();
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (salary) { //TODO1
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

运行结果:

Thread-0 start at:1452076058

Thread-1 start at:1452076059

Thread-0 has entered method1 at:1452076063

Thread-0 end at:1452076063

Thread-1 has entered method2 at:1452076063

Thread-1 end at:1452076063


从运行结果可以看出,当线程t1进入方法method1 5秒之后(释放了对象锁),线程t2才进入方法method2,说明两个线程持有的是相同的对象锁。


二、synchronized修饰静态方法:持有Salary.class的对象锁

并发对象类Salary

public class Salary {
	public synchronized static void method1(){
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
	}
	
	public void method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

并发程序SynchronizeTest

public class SynchronizeTest {

	public static void main(String[] args) {
		final Salary salary = new Salary();
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				Salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (Salary.class) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

运行结果:

Thread-0 start at:1452076058

Thread-1 start at:1452076059

Thread-0 has entered method1 at:1452076063

Thread-0 end at:1452076063

Thread-1 has entered method2 at:1452076063

Thread-1 end at:1452076063


三、synchronized(对象1):持有对象的对象锁

并发对象类Salary:

public class Salary {
	private Object lock = new Object();
	public Salary(Object lock){
		this.lock = lock;
	}
	public void method1(){
		synchronized (lock) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
		}
	}
	
	public void  method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

并发程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Object 对象1 = new Object();
		final Salary salary = new Salary(对象1);
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (对象1) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

运行结果:

Thread-0 start at:1452077315

Thread-1 start at:1452077316

Thread-0 has entered method1 at:1452077320

Thread-1 has entered method2 at:1452077320

Thread-1 end at:1452077320

Thread-0 end at:1452077320


四、  synchronized(this):持有Salary类当前对象实例的对象锁

并发对象类Salary:

public class Salary {
	private Object lock = new Object();
	public Salary(Object lock){
		this.lock = lock;
	}
	public void method1(){
		synchronized (this) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
		}
	}
	
	public void method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}


并发程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Object 对象1 = new Object();
		final Salary salary = new Salary(对象1);
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (salary) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

运行结果:

Thread-0 start at:1452077439

Thread-1 start at:1452077440

Thread-0 has entered method1 at:1452077444

Thread-0 end at:1452077444

Thread-1 has entered method2 at:1452077444

Thread-1 end at:1452077444


五、  synchronized(Salary.class): 持有Salary.class的对象锁

与测试程序二同理。


总结一下,synchronized的使用变种可能还有很多,但是万变不离其宗,分析多线程是否在synchronized处产生了互斥,其根本就是要分析synchronized所持有的对象锁是否为同一个对象锁。这很关键!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章