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();
}
}