在多线程操作数据时需要考虑使用synchronized关键词修饰属性或方法,同步锁synchronized大致可分为两类:对象锁、全局锁。在分析同步锁代码时需要关心两个问题:锁的对象是谁、谁持有锁。
①对象锁:
当一个线程访问对象object的一个synchronized(this)同步代码块或synchronized修饰的实例方法时,其他线程对该object中所有其它synchronized(this)同步代码块或synchronized修饰的实例方法的访问将被阻塞。
public class Demo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
for(int i = 0;i < 3;i++) {
Thread t = new Thread(myRunnable);
t.start();
}
}
}
class MyRunnable implements Runnable{
static int i;
@Override
public void run() {
add();
}
synchronized void add() {
System.out.println(Thread.currentThread().getName()+"开始,i = "+i);
i++;
System.out.println(Thread.currentThread().getName()+"结束,i = "+i);
}
}
输出结果:
在上面的例子中,synchronized 修饰实例方法,锁的对象是myRunnable,持有锁的为当前执行该方法的线程。循环创建并启动的三个线程都调用myRunnable对象的同步add()方法,当其中某个线程执行add()方法时需要先获得myRunnable对象的锁,若对象锁被其他线程占用,该线程将阻塞在此,等待锁的获得。
②全局锁
当一个线程访问类的一个synchronized(类名.class)同步代码块或synchronized修饰的类方法时,其他线程对该类中所有其它synchronized(类名.class)同步代码块或synchronized修饰的类方法的访问将被阻塞。
public class Demo {
public static void main(String[] args) {
for(int i = 0;i < 3;i++) {
Thread t = new Thread() {
@Override
public void run() {
Sync.add();
}
};
t.start();
}
}
}
class Sync{
static int i;
synchronized static void add() {
System.out.println(Thread.currentThread().getName()+"开始,i = "+i);
i++;
System.out.println(Thread.currentThread().getName()+"结束,i = "+i);
}
}
输出结果:
在上面的例子中,synchronized 修饰类方法,锁的对象是Sync.class,持有锁的为当前执行该方法的线程。循环创建并启动的三个线程都调用Sync类的静态方法add(),当其中某个线程执行add()方法时需要先获得Sync类的锁,若对象锁被其他线程占用,该线程将阻塞在此,等待锁的获得。
总结:
1.使用synchronized对象锁时,线程需要拿到的是该对象的锁,否则将无法操作该对象中所有带有对象锁的属性和方法。
2.使用全局锁时,需要拿到的是类文件的锁,否则将无法操作该类所有带全局锁的属性和方法。