在自己学习的过程中,把自己的理解和体会记录下来。希望能帮助到当时和我一样懵逼的人。
synchronized 用过的人都知道是锁,那么锁的概念是什么呢?
换老马的话就是 锁就是厕所里的一个坑,外面很多人排队(一大堆阻塞线程)等着,如果你要用这个坑,你就得先拿到这个坑门的钥匙,然后打开这个门,然后才能办你的事。这是我的理解。
synchronized 用到的地方很多。
//方法上/this关键字 锁定的是当前对象
synchronized void m1(){}
synchronized (this)
//****特别注意千万不要这么写 String a = "xxx" ;千万不要锁字符串,可能会造成锁冲突而导致很恐怖的Bug
//某一对象 注意这个地方锁的对象是在堆里的对象 而不是o这个引用 一旦你改变了引用 那么锁自然而然就释放了
Object o = new Object();
synchronized ( o ) {}
//锁定静态方法或属性 那么锁定的就是class对象
synchronized static ......
加上synchronized后,表示的是当前代码块同一时刻只能有一个线程能执行,即同步。各个线程会互相竞争这把锁,拿到锁你就进入并执行,如果拿不到那么就一直阻塞,直到拿到这把锁。
接下来看例子
import java.util.concurrent.TimeUnit;
/**
* @author Qiu Ping
* Knowledge: synchronized
* Desc: 多个访问访问同一资源,不加锁会出现问题。
*/
public class o1synchronized {
public static void main(String [] args) throws InterruptedException {
m1 m = new m1();
new Thread(() -> {
for(int i=0; i<3; i++) {
m.m();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
new Thread(() -> {
for(int i=0; i<3; i++) {
m.m();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for(int i=0; i<3; i++) {
m.m();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
TimeUnit.SECONDS.sleep(6);
System.out.println(m.getcount());
}
}
class m1{
private int count = 0;
//先不加synchronized
synchronized void m() {
count++;
System.out.println(Thread.currentThread().getName()+"线程执行了+1操作,当前count为:"+this.count);
try {
TimeUnit.MILLISECONDS.sleep(500);;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
int getcount(){
return this.count;
}
}
执行结果
没有规律,同一时刻多个现场访问这一资源,造成冲突。接下来我们加上synchronized.
多个线程同时访问一个枷锁的对象的时候,只能有一个拿到锁的才能操作。
synchronized是可重入的
我拿到这个锁后,可以重复获得这把锁。
我们来试一下。
synchronized void m() {
synchronized(this){
count++;
System.out.println(Thread.currentThread().getName()+"线程执行了+1操作,当前count为:"+this.count);
try {
TimeUnit.MILLISECONDS.sleep(500);;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
把方法改成这样,还是可以继续执行,就说明synchronized是可重入的。
synchronized出现异常后,自动释放锁。
/**
* @author Qiu Ping
* synchronized 出现异常自动释放锁
*/
public class o2exception {
public static void main(String [] args){
m a = new m();
new Thread(a::m1,"一").start();
new Thread(a::m2,"二").start();
}
}
class m{
synchronized void m1(){
for(int i=0; i<5; i++){
System.out.println(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(i == 3){
int a = i/0;
}
}
}
synchronized void m2(){
System.out.println("我是m2");
}
}
执行结果
当i=3的时候,我们抛出异常,然后释放了锁,线程二得以执行。