每天一例多线程[day6]-----synchronized锁的粒度控制
发布日期:2021-06-30 13:45:00 浏览次数:2 分类:技术文章

本文共 7545 字,大约阅读时间需要 25 分钟。

syhchronized写法举例

package com.jeff.base.sync006;/** * 使用synchronized声明的方法在某些情况下是有弊端的, * 比如:A线程调用synchronized同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的事件才能执行, * 这样的情况下可以使用synchronized去优化代码的执行时间,也就是通常所说的减小锁的粒度。 *  * synchronized可以使用任意的Object进行加锁,用法比较灵活。 *  * 使用synchronized代码块加锁,比较灵活 * @author jeff * */public class ObjectLock {	public void method1(){		synchronized (this) {	//对象锁			try {				System.out.println("do method1..");				Thread.sleep(2000);			} catch (InterruptedException e) {				e.printStackTrace();			}		}	}		public void method2(){		//类锁		synchronized (ObjectLock.class) {			try {				System.out.println("do method2..");				Thread.sleep(2000);			} catch (InterruptedException e) {				e.printStackTrace();			}		}	}		private Object lock = new Object();	public void method3(){		//任何对象锁		synchronized (lock) {			try {				System.out.println("do method3..");				Thread.sleep(2000);			} catch (InterruptedException e) {				e.printStackTrace();			}		}	}			public static void main(String[] args) {				final ObjectLock objLock = new ObjectLock();		Thread t1 = new Thread(new Runnable() {			@Override			public void run() {				objLock.method1();			}		});		Thread t2 = new Thread(new Runnable() {			@Override			public void run() {				objLock.method2();			}		});		Thread t3 = new Thread(new Runnable() {			@Override			public void run() {				objLock.method3();			}		});				t1.start();		t2.start();		t3.start();					}	}

String常量锁

package com.jeff.base.sync006;/** * synchronized代码块对字符串的锁,注意String常量池的缓存功能 * @author jeff * */public class StringLock {	public void method() {		//new String("字符串常量")		synchronized ("字符串常量") {			try {				while(true){					System.out.println("当前线程 : "  + Thread.currentThread().getName() + "开始");					Thread.sleep(1000);							System.out.println("当前线程 : "  + Thread.currentThread().getName() + "结束");				}			} catch (InterruptedException e) {				e.printStackTrace();			}		}	}		public static void main(String[] args) {		final StringLock stringLock = new StringLock();		Thread t1 = new Thread(new Runnable() {			@Override			public void run() {				stringLock.method();			}		},"t1");		Thread t2 = new Thread(new Runnable() {			@Override			public void run() {				stringLock.method();			}		},"t2");				t1.start();		t2.start();	}}

打印:

当前线程 : t1开始当前线程 : t1结束当前线程 : t1开始当前线程 : t1结束.......................

一直就执行了t1的线程,原因是对于字符串常量,在常量池中引用是不变的,所以线程会永远持有String常量锁而不释放。

如果想让t2执行且使用String锁,可以把

new String("字符串常量")

这样就可以让t2和t1同时执行,是两把独立的锁。

 

改变常量锁

package com.jeff.base.sync006;/** * 锁对象的改变问题 * @author jeff * */public class ChangeLock {	private String lock = "lock";		private void method(){		synchronized (lock) {			try {				System.out.println("当前线程 : "  + Thread.currentThread().getName() + "开始");				lock = "change lock";				Thread.sleep(2000);				System.out.println("当前线程 : "  + Thread.currentThread().getName() + "结束");			} catch (InterruptedException e) {				e.printStackTrace();			}		}	}		public static void main(String[] args) {			final ChangeLock changeLock = new ChangeLock();		Thread t1 = new Thread(new Runnable() {			@Override			public void run() {				changeLock.method();			}		},"t1");		Thread t2 = new Thread(new Runnable() {			@Override			public void run() {				changeLock.method();			}		},"t2");		t1.start();		try {			Thread.sleep(100);		} catch (InterruptedException e) {			e.printStackTrace();		}		t2.start();	}	}

在t1线程执行的过程中,t1所持有的锁常量“lock“,被改变,导致锁被突然释放,t2线程获得了锁。

lock = "change lock";

打印:

         当前线程 : t1开始

         当前线程 : t2开始
         当前线程 : t1结束
         当前线程 : t2结束

 

那么,对象的属性被修改,对象锁会不会被释放?

改变对象锁属性

package com.jeff.base.sync006;/** * 同一对象属性的修改不会影响锁的情况 * @author jeff * */public class ModifyLock {		private String name ;	private int age ;		public String getName() {		return name;	}	public void setName(String name) {		this.name = name;	}	public int getAge() {		return age;	}	public void setAge(int age) {		this.age = age;	}		public synchronized void changeAttributte(String name, int age) {		try {			System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 开始");			this.setName(name);			this.setAge(age);						System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 修改对象内容为: " 					+ this.getName() + ", " + this.getAge());						Thread.sleep(2000);			System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 结束");		} catch (InterruptedException e) {			e.printStackTrace();		}	}		public static void main(String[] args) {		final ModifyLock modifyLock = new ModifyLock();		Thread t1 = new Thread(new Runnable() {			@Override			public void run() {				modifyLock.changeAttributte("张三", 20);			}		},"t1");		Thread t2 = new Thread(new Runnable() {			@Override			public void run() {				modifyLock.changeAttributte("李四", 21);			}		},"t2");				t1.start();		try {			Thread.sleep(100);		} catch (InterruptedException e) {			e.printStackTrace();		}		t2.start();	}	}

打印:

当前线程 : t1 开始

当前线程 : t1 修改对象内容为: 张三, 20
当前线程 : t1 结束

当前线程 : t2 开始

当前线程 : t2 修改对象内容为: 李四, 21
当前线程 : t2 结束

t1执行完,t2才开始执行,说明对象属性的改变不会影响线程对锁的持有

 

乐观锁(代码块粒度)

package com.jeff.base.sync006;/** * 使用synchronized代码块减小锁的粒度,提高性能 * @author jeff * */public class Optimize {	public void doLongTimeTask(){		try {						System.out.println("当前线程开始:" + Thread.currentThread().getName() + 					", 正在执行一个较长时间的业务操作,其内容不需要同步");			Thread.sleep(2000);						synchronized(this){				System.out.println("当前线程:" + Thread.currentThread().getName() + 					", 执行同步代码块,对其同步变量进行操作");				Thread.sleep(1000);			}			System.out.println("当前线程结束:" + Thread.currentThread().getName() +					", 执行完毕");					} catch (InterruptedException e) {			e.printStackTrace();		}	}		public static void main(String[] args) {		final Optimize otz = new Optimize();		Thread t1 = new Thread(new Runnable() {			@Override			public void run() {				otz.doLongTimeTask();			}		},"t1");		Thread t2 = new Thread(new Runnable() {			@Override			public void run() {				otz.doLongTimeTask();			}		},"t2");		t1.start();		t2.start();			}		}

打印:

当前线程开始:t1, 正在执行一个较长时间的业务操作,其内容不需要同步当前线程开始:t2, 正在执行一个较长时间的业务操作,其内容不需要同步
当前线程:t2, 执行同步代码块,对其同步变量进行操作当前线程结束:t2, 执行完毕
当前线程:t1, 执行同步代码块,对其同步变量进行操作当前线程结束:t1, 执行完毕

分析:一个操作内,大部分操作不需要同步,异步即可满足,而只有其中一小步需要进行同步控制,比如修改数据库的一条数据,那么仅仅使用代码块对这个操作进行加锁即可。(数据库使用版本号也可以实现乐观锁,update时比对前一步查出来的版本号是否被修改,如果没被修改则执行修改,否则重试)。

 

死锁

多个线程相互持有对方的锁不释放,导致相互等待,出现死锁。

 

package com.jeff.base.sync006;/** * 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况 * @author jeff * */public class DeadLock implements Runnable{	private String tag;	private static Object lock1 = new Object();	private static Object lock2 = new Object();		public void setTag(String tag){		this.tag = tag;	}		@Override	public void run() {		if(tag.equals("a")){			synchronized (lock1) {				try {					System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");					Thread.sleep(2000);				} catch (InterruptedException e) {					e.printStackTrace();				}				synchronized (lock2) {					System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");				}			}		}		if(tag.equals("b")){			synchronized (lock2) {				try {					System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");					Thread.sleep(2000);				} catch (InterruptedException e) {					e.printStackTrace();				}				synchronized (lock1) {					System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");				}			}		}	}		public static void main(String[] args) {				DeadLock d1 = new DeadLock();		d1.setTag("a");		DeadLock d2 = new DeadLock();		d2.setTag("b");		 		Thread t1 = new Thread(d1, "t1");		Thread t2 = new Thread(d2, "t2");		 		t1.start();		try {			Thread.sleep(500);		} catch (InterruptedException e) {			e.printStackTrace();		}		t2.start();	}		}

 

 

 

 

 

 

 

 

转载地址:https://jeffsheng.blog.csdn.net/article/details/80558371 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:每天一例多线程[day8]-----AutomicInteger原子操作
下一篇:每天一例多线程[day5]-----synchronized锁重入

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月18日 04时51分09秒