架构系列——使用synchronized需要注意哪些细节

目录

前言

1、synchronized锁的重入性

2、不要使用字符串常量作为锁

3、锁对象的改变问题


前言

synchronized可以为任意对象加锁,用法比较灵活,语法如下

(1)修饰代码块,作用于调用的对象;

(2)修饰方法,作用于调用的对象;

(3)修饰静态方法,作用于所有对象;

(4)修饰类,作用于所有对象。

synchronized取得的锁都是对象锁,而不是把一段代码(或者方法)当成锁!

使用synchronized时,应该注意以下细节问题:

1、synchronized锁的重入性

如下列代码所示,类似于ReentrantLock

在method1中没有释放锁的情况下,可以继续调用synchronized修饰的method2

public class SyncDubbo {
	public synchronized void method1(){
		System.out.println("method1...");
		method2();
	}
	public synchronized void method2(){
		System.out.println("method2...");
		method3();
	}
	public synchronized void method3(){
		System.out.println("method1...");
	}
	
	public static void main(String[] args) {
		final SyncDubbo sd = new SyncDubbo();
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				sd.method1();
			}
			
		}, "t1");
		t1.start();
	}
}

 如下列代码所示,在子类与父类之间相互调用也运用了synchronized的重用性

public class FatherSon {
	static class Father {
		public int num = 10;
		public synchronized void method1(){
			try {
				num --;
				System.out.println("Father num = " + num);
				Thread.sleep(100);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	
	static class Son extends Father {
		public synchronized void method2(){
			try {
				while (num >0) {
					num --;
					System.out.println("Son num = " + num);
					Thread.sleep(100);
					this.method1();
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Son son = new Son();
				son.method2();
			}
		}, "t1");
		t1.start();
	}
}

2、不要使用字符串常量作为锁

如下列代码所示,使用了字符串常量作为锁,那么t1和t2运行之后将会一直在t1中出现死循环,t2永远拿不到锁!

解决:可以使用 new String("字符串常量") 作为锁

public class StringLock {
	public void method(){
		synchronized("字符串常量"){
			try {
				while (true){
					System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
					Thread.sleep(1000);
					System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	
	public static void main(String[] args) {
		final StringLock sl = new StringLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				sl.method();
			}
			
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				sl.method();
			}
			
		}, "t2");
		t1.start();
		t2.start();
	}
}

3、锁对象的改变问题

如下列代码所示,使用synchronized锁住了一个对象,并且在代码里重新new了一个对象,导致锁对象改变。

这样在t1还没有运行完代码的时候,t2就已经可以拿到锁了,显然会出现问题!

解决:不要改变锁对象(但是改变对象的属性对代码不会有影响,比如锁是一个人,那么可以改变这个人的身高、年龄等)

public class ChangeLock {
	Object obj = new Object();
	
	public void method(){
		synchronized(obj){
			try {
				System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
				//从这里改变锁对象
				obj = new Object();
				Thread.sleep(3000);
				System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	public static void main(String[] args) {
		final ChangeLock cl = new ChangeLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				cl.method();
			}
			
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				cl.method();
			}
			
		}, "t2");
		t1.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();
	}
}
发布了131 篇原创文章 · 获赞 1663 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/qq_26230421/article/details/104264397
今日推荐