欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Synchronized

程序员文章站 2022-07-12 20:35:34
...
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();
	}

}




相关标签: thread