syhchronized写法举例
-
package com.jeff.base.sync006;
-
/**
-
* 使用synchronized声明的方法在某些情况下是有弊端的,
-
* 比如:A线程调用同步的方法执行一个很长时间的任务,那么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时比对前一步查出来的版本号是否被修改,如果没被修改则执行修改,否则重试)。
死锁
多个线程相互持有对方的锁不释放,导致相互等待,出现死锁。
[java] view plain copy
- <code class="language-java">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();
- }
- }
- </code>