synchronized关键字英文直译过来为“同步”,同步在java多线程里也可以理解为“排队”。当然它也有一个优雅的名字“互斥锁”。
synchronized可以对共享资源进行锁定,同时一刻只有一个线程可以操作它,等一个线程操作完了,再把锁释放,让下一个线程操作。这样可以保证“线程安全”。
其实结合现实生活我们不难理解,比如一辆车在同一时刻的驾驶员只有一个,能操作这辆车的也只有驾驶员自己,要是有两个人同一时间开同一辆车的话,你转一下方向盘,他转一下方向盘,那不就乱套了吗,肯定是要出事故的呀。
放在java中,如果有一个资源 i 是被线程A和线程B共享的,两个线程都要对其进行i++操作。我们知道i++这个表达式并不是原子性的,要先读取i的值,再修改。正常情况下两个线程都对其i++了,那么结果应该是2。
如果有这么一种情况 i 的初始值为0,线程A读取i的值还没来得及进行修改,线程B就已经读取到i的值了,并且在A之前进行修改,这时 i 的值被B修改为1,B修改完A才进行修改操作,那么这时A读取到的值还是B做修改之前的值0,那么此时A做修改只会基于一开始读取到的0修改加一。那么线程A和B进行的修改都是基于初始值为0修改的。这样就造成了数据错误。
如何解决这种线程不安全的问题呢?用synchronized关键字。
synchronized关键字最主要有以下3种应用方式
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
package tongbu;
public class one implements Runnable{
static int i=0;
@Override
public void run() {
for (int j=1;j<=10000;j++){
jia();
}
}
public synchronized void jia(){
i++;
}
public static void main(String[] args) throws InterruptedException {
one one= new one();
Thread t1=new Thread(one);
Thread t2=new Thread(one);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
package tongbu;
public class one implements Runnable{
static int i=0;
@Override
public void run() {
for (int j=1;j<=10000;j++){
jia();
}
}
public static synchronized void jia(){
i++;
}
public static void main(String[] args) throws InterruptedException {
//one one= new one();
Thread t1=new Thread(new one());
Thread t2=new Thread(new one());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
修饰静态方法和实例方法的区别是,前者可以实例化两个对象,后者只能实例化一个对象。
前者看似实例化两个对象,但由于jia()是static修饰的,两个对象操作的都是jvm里的同一个方法。
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
package tongbu;
public class one implements Runnable{
static one one=new one();
static int i=0;
@Override
public void run() {
synchronized (one){
for (int j=1;j<=10000;j++){
i++;
}}
}
public static void main(String[] args) throws InterruptedException {
//one one= new one();
Thread t1=new Thread(one);
Thread t2=new Thread(one);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
synchronized括号里放共享资源,可以是this,类的实例化对象。
因为同步锁锁的是共享的资源嘛,所以当然要放共享资源啦。