总结一下synchronized的用法:
1、修饰静态方法
2、修饰实例方法
3、修饰代码块
一、首先看一下修饰静态方法和修饰实例方法的区别,直接上代码:
/*
* synchronized:修饰实例方法/修饰静态方法
*/
public class SynchronizedTest2 {
public static void main(String[] args)
throws Exception {
AccountingSyncClass accountingSyncClass = new AccountingSyncClass();
Thread t1 = new Thread(new AccountingSyncClass());
Thread t2 = new Thread(new AccountingSyncClass());
t1.start();
t2.start();
t2.join();
t1.join();
System.out.println(accountingSyncClass.i);
}
public void getName2() {
System.out.println(SynchronizedTest2.class.getName());
}
}
class AccountingSyncClass implements Runnable {
static int i = 0;
static boolean flag = true;
// 作用于静态方法,锁是当前class对象,也就是AccountingSyncClass类对应的class对象
public static synchronized void increase() {
i++;
}
// 非静态方法,锁定当前实例对象
public synchronized void increase2() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
// increase();
increase2();
}
}
}
1、当run()方法中调用increase2(),main方法中两个线程如果访问的是同一个AccountingSyncClass的实例对象,则答应结果为2000000,如果是两个不同的实例对象,则结果不是2000000;
2、当run()方法中调用的是increase() [synchronized修饰的静态方法],main方法启动的线程无论访问的是否是同一个AccountingSyncClass的实例对象,结果都是2000000;
3、如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问static synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁;
二、下面看一下synchronized如何修饰代码块,优势:比修饰整个方法更加灵活!
public class SychronizedTest {
public static boolean flag = true;
public static StringBuffer s1 = new StringBuffer("123");
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
while (flag) {
synchronized (s1) {
try {
s1.wait();
System.out.println("t1 awake!");
flag = false;
} catch (InterruptedException e) {
}
}
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
synchronized (s1) {
System.out.println(s1);
s1.notify();
}
}
}, "t2");
t1.start();
t2.start();
}
}
1、flag:用来终止线程;
2、线程t1和t2启动,t1先拿到了s1的锁,调用s1.wait()方法,释放对s1的锁;
3、线程t2获得s1的锁,执行方法块,执行完成调用notify(),叫醒等待在s1对象上的其他线程(t1线程),并释放s1的锁。t2拿到s1的锁继续执行。
必须注意的地方:
1、某个对象实例内,synchronized aMethod(){}关键字可以防止多个线程访问对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法.
2、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
3、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
4、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
推荐博客:https://blog.youkuaiyun.com/javazejian/article/details/72828483,这篇博客讲的非常好!