理解synchronized的含义
synchronized是实现java同步机制的锁,线程进入同步代码块和方法是,会获取该锁,当结束同步代码块和方法时会释放该锁。当同步代码块和方法加锁时,只允许一个线程对它进行访问,另一个线程若想对它访问必须等上一个进程释放该锁,下一个线程才会获得该锁。
修饰普通方法
修饰普通方法:是对实例化对象加锁。
public class SynchronizedDemo {
public static void main(String[] args) throws Exception {
ClassA a = new ClassA();
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(a.i);
}
}
public class ClassA implements Runnable {
static int i = 0;
public synchronized void a() {
i++;
}
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
a();
}
}
}
实例化了一个ClassA的实例,并创建了两个线程,若未加同步锁,第二个线程在第一个线程读取旧值并会写新值之前读取i的值,就会造成线程安全失败。所以a方法必须加锁,保证线程安全。对a方法加synchronized其实就是就ClassA的的锁。
修饰静态方法
修饰静态方法:是对类对象加锁。
修改SynchronizedDemo 类中的代码:
public class SynchronizedDemo {
public static void main(String[] args) throws Exception {
ClassA a1 = new ClassA();
ClassA a2 = new ClassA();
Thread t1 = new Thread(a1);
Thread t2 = new Thread(a2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(ClassA.i);
}
}
若是创建两个实例并开启两个线程去访问a方法,也会造成线程安全失败的问题,因为创建了两个实例化对象,意味着有两个不同的实例的对象锁。尽管在a方法上加了synchroinized也会造成线程安全失败。解决这个问题就需要在类对象加锁,无论无论创建多少个实例但是类对象只有一个,需要synchronized加在静态方法上。
public class ClassA implements Runnable {
static int i = 0;
public static synchronized void a() {
i++;
}
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
a();
}
}
}
修饰同步代码块
修饰同步代码块:是对指定对象加锁。
public class SynchronizedDemo {
public static void main(String[] args) throws Exception {
ClassA a = new ClassA();
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(a.i);
}
}
public class ClassA implements Runnable {
static int i = 0;
public void a() {
i++;
}
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 2000; i++) {
a();
}
}
}
}
修饰同步代码块对当前对象加锁,当有其他携带该对象的锁的线程进入synchronized修饰的代码块时,必须等当前线程释放该锁之后才可以进入。