`

Synchronized

阅读更多
Synchronized


一、锁重入
1.概念

关键字 synchronized 拥有锁重入功能,也就是在使用 synchronized 时,当一个线程得到了一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。

2.示例

三个方法,依次调用,获取 第一个方法的锁,执行第二个方法时同样可以获取锁

package com.study.current.thread.day01;

/**
 * 锁重入的机制
 * 在获取 m1 的锁后获取 m2 的锁
 */
public class SynchronizedDubo1 extends Thread {

	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("method3");
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final SynchronizedDubo1 thread = new SynchronizedDubo1();
		Thread t = new Thread(new Runnable() {
			
			public void run() {
				thread.method1();
			}
		});
		t.start();
	}

}



二、父子类

父子类间继承时,使用synchronized 保证线程安全
package com.study.current.thread.day01;

/**
 * 父子关系调用
 */
public class SynchronizedDubo2 extends Thread {

	static class Main{
		public int i = 10 ;
		public synchronized void operationSup(){
			i -- ;
			System.out.println("Main i : "+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} 
		
	}
	
	static class SubMain extends Main{
		public synchronized void operationSub(){
			while(i> 0){
				i -- ;
				System.out.println("SubMain i : "+i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				this.operationSup();
			}
		}
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new Thread(new Runnable() {
			
			public void run() {
				SubMain syn = new SubMain();
				syn.operationSub();
			}
		});
		thread.start();
	}

}



三、异常

当发生异常时,或中断运行(异常数据对后续功能有影响),或继续运行但保存错误数据的日志

package com.study.current.thread.day01;

public class SynchronizedException extends Thread {

	public int count = 0 ;
	
	/**
	 * 注意此处的捕获异常,级别为 Exception 即可以捕获此时的两种 Exception
	 * 执行结果:出现异常,但程序依然在运行
	 */
	public synchronized void operation(){
		while(true){
			count ++ ;
			try {
				Thread.sleep(1000);
				System.out.println(Thread.currentThread().getName()+" count : "+count);
				if(count == 10){
					System.out.println(Integer.valueOf("a"));
				}
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("log log 10 exception");
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final SynchronizedException exc = new SynchronizedException();
		
		Thread t = new Thread(new Runnable() {
			
			public void run() {
				exc.operation();
			}
		},"t1");
		
		t.start();
	}

}



四、锁种类

使用 synchronized 声明的方法在某些情况下是由弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下可以使用 synchronized 代码块去优化代码执行时间,即减小锁的粒度


锁:
当前对象作为锁
类作为锁
任意对象作为锁
package com.study.current.thread.day01;

/**
 * 锁对象
 * 1.当前对象锁
 * 2.类锁
 * 3.任意对象锁
 */
public class ObjectLock extends Thread {

	public void method1(){
		synchronized (this) { // 对象锁,this 指代当前对象
			System.out.println("method1");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void method2(){
		synchronized (ObjectLock.class) {
			System.out.println("method2"); // 类锁
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private Object ojt = new Object();
	public void method3(){
		synchronized (ojt) { // 任意对象锁
			System.out.println("method3");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final ObjectLock lock = new ObjectLock();
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				lock.method1();
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {

				lock.method2();
			}
		});
		
		Thread t3 = new Thread(new Runnable() {
			
			public void run() {
				lock.method3();
			}
		});
		
		t1.start();
		t2.start();
		t3.start();
	}

}



五、String 常量不能作为锁

String 常量作为锁,会造成死循环,锁失效
package com.study.current.thread.day01;

/**
 * String 常量作为锁,会出现死循环
 */
public class StringLock {

	public void method(){
		/**
		 * 使用字符串常量当做锁,会出现死循环,即当前的运行结果总是  t1 的线程
		 * 字符串常量只有一个引用
		 * 解决:
		 * 可以替换为 new String("") 
		 */
		synchronized ("字符串常量") {
			System.out.println("method");
			try {
				while(true){
					System.out.println(Thread.currentThread().getName()+" start");
					Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName()+" end");
					
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final StringLock stringLock = new StringLock();
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				stringLock.method();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
				stringLock.method();
			}
		},"t2");
		t1.start();
		t2.start();
	}

}




六、不要试图改变锁

改变当前运行的锁,会造成锁的失效

package com.study.current.thread.day01;

/**
 * 不要修改锁对象
 * 会导致锁失效
 * 开始几个线程争夺 a 锁, 第一个线程 获取 a ,并把 锁改为 b ,第二个线程则无需等待 a 锁的释放,直接获取 b 锁,
 * 就不能起到锁的作用
 */
public class ChangeLock {

	private String lock = "abc";
	public void changeLock(){
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " start");
			try {
				lock = "bcd" ;
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" end");
			
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final ChangeLock lock = new ChangeLock();
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				lock.changeLock();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
				lock.changeLock();
			}
		},"t2");
		
		t1.start();
		t2.start();
	}

}




七、对象锁,其属性值得改变不影响锁的使用

锁对象的改变问题,当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那么持有的锁就不同。
如果对象本身不发生改变,那么依然是同步的,即使是对象的属性发生了改变。

package com.study.current.thread.day01;

/**
 * 对象的属性值的变化,不影响锁的使用
 */
public class ModifyLock {

	private String name ;
	private String pass ;
	
	public synchronized void changeAttribute(String name,String pass){
		System.out.println(Thread.currentThread().getName() + " start");
		this.setName(name);
		this.setPass(pass);
		System.out.println(Thread.currentThread().getName() + " modify name:"+name+" pass:"+pass);
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " end");
	}
	
	
	
	
	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}


	public String getPass() {
		return pass;
	}


	public void setPass(String pass) {
		this.pass = pass;
	}


	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final ModifyLock lock = new ModifyLock();
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				lock.changeAttribute("zhangsan", "1111");
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
				lock.changeAttribute("lisi", "2222");
			}
		},"t2");
		
		t1.start();
		t2.start();
	}

}




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics