Java 中 Synchronized 的用法(简略介绍)
简略介绍
synchronized 是 Java 中的关键字,是一种同步锁。它润饰的对象有以下几种:
1. 润饰一个代码块,被润饰的代码块称为同步语句块,其作用的范畴是大括号{} 括起来的代码,作用的对象是调用这个代码块的对象;
2. 润饰一个办法,被润饰的办法称为同步办法,其作用的范畴是整个办法,作用的对象是调用这个办法的对象;
3. 批改一个动态的办法,其作用的范畴是整个静态方法,作用的对象是这个类的所有对象;
4. 批改一个类,其作用的范畴是 synchronized 前面括号括起来的局部,作用主的对象是这个类的所有对象。
润饰一个代码块
1、一个线程拜访一个对象中的 synchronized(this) 同步代码块时,其余试图拜访该对象的线程将被阻塞。
例子:
View Code
应用关键字 synchronized 运行后果
不应用关键字 synchronized 运行后果
当两个并发线程 (thread1 和 thread2) 拜访同一个对象 (syncThread) 中的 synchronized 代码块时,在同一时刻只能有一个线程失去执行,另一个线程受阻塞,必须期待以后线程执行完这个代码块当前能力执行该代码块。Thread1 和 thread2 是互斥的,因为在执行 synchronized 代码块时会锁定以后的对象,只有执行完该代码块能力开释该对象锁,下一个线程能力执行并锁定该对象。
咱们再把 SyncThread 的调用略微改一下:
复制代码
public static void main(String[] args) {
System.out.println("应用关键字 synchronized 每次调用进行 new SyncThread()");
Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();}
复制代码
运行后果
为什么下面的例子中 thread1 和 thread2 同时在执行。这是因为 synchronized 只锁定对象,每个对象只有一个锁(lock)与之相关联,而下面的代码等同于上面这段代码:
复制代码
public static void main(String[] args) {
System.out.println("应用关键字 synchronized 每次调用进行 new SyncThread()");
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();}
复制代码
这时创立了两个 SyncThread 的对象 syncThread1 和 syncThread2,线程 thread1 执行的是 syncThread1 对象中的 synchronized 代码(run),而线程 thread2 执行的是 syncThread2 对象中的 synchronized 代码(run);咱们晓得 synchronized 锁定的是对象,这时会有两把锁别离锁定 syncThread1 对象和 syncThread2 对象,而这两把锁是互不烦扰的,不造成互斥,所以两个线程能够同时执行。
二、当一个线程拜访对象的一个 synchronized(this)同步代码块时,另一个线程依然能够拜访该对象中的非 synchronized(this)同步代码块。
多个线程拜访 synchronized 和非 synchronized 代码块
View Code
运行后果
下面代码中 countAdd 是一个 synchronized 的,printCount 是非 synchronized 的。从下面的后果中能够看出一个线程拜访一个对象的 synchronized 代码块时,别的线程能够拜访该对象的非 synchronized 代码块而不受阻塞。
润饰一个办法
Synchronized 润饰一个办法很简略,就是在办法的后面加 synchronized,public synchronized void method(){}; synchronized 润饰办法和润饰一个代码块相似,只是作用范畴不一样,润饰代码块是大括号括起来的范畴,而润饰办法范畴是整个函数。如将的 run 办法改成如下的形式,实现的成果一样。
复制代码
public synchronized void run() {
{for (int i = 0; i < 5; i++) {
try {System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
}
复制代码
运行后果
Synchronized 作用于整个办法的写法。
写法一:
public synchronized void method()
{
// todo
}
写法二:
复制代码
public void method()
{
synchronized(this) {
}
}
复制代码
写法一润饰的是一个办法,写法二润饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个办法时的内容。
在用 synchronized 润饰办法时要留神以下几点:
- synchronized 关键字不能继承。
尽管能够应用 synchronized 来定义方法,但 synchronized 并不属于办法定义的一部分,因而,synchronized 关键字不能被继承。如果在父类中的某个办法应用了 synchronized 关键字,而在子类中笼罩了这个办法,在子类中的这个办法默认状况下并不是同步的,而必须显式地在子类的这个办法中加上 synchronized 关键字才能够。当然,还能够在子类办法中调用父类中相应的办法,这样尽管子类中的办法不是同步的,但子类调用了父类的同步办法,因而,子类的办法也就相当于同步了。这两种形式的例子代码如下:
在子类办法中加上 synchronized 关键字
复制代码
lass Parent {
public synchronized void method() {}
}
class Child extends Parent {
public synchronized void method() {}
}
复制代码
在子类办法中调用父类的同步办法
复制代码
class Parent {
public synchronized void method() {}
}
class Child extends Parent {
public void method() { super.method(); }
}
复制代码
在定义接口办法时不能应用 synchronized 关键字。
构造方法不能应用 synchronized 关键字,但能够应用 synchronized 代码块来进行同步。
润饰一个动态的办法
Synchronized 也可润饰一个静态方法,用法如下:
public synchronized static void method() {
}
咱们晓得静态方法是属于类的而不属于对象的。同样的,synchronized 润饰的静态方法锁定的是这个类的所有对象。
synchronized 润饰静态方法
View Code
运行后果
syncThread1 和 syncThread2 是 SyncThread 的两个对象,但在 thread1 和 thread2 并发执行时却放弃了线程同步。这是因为 run 中调用了静态方法 method,而静态方法是属于类的,所以 syncThread1 和 syncThread2 相当于用了同一把锁。这与应用关键字 synchronized 运行后果雷同
润饰一个类
Synchronized 还可作用于一个类,用法如下:
View Code
运行后果
成果和下面 synchronized 润饰静态方法是一样的,synchronized 作用于一个类 T 时,是给这个类 T 加锁,T 的所有对象用的是同一把锁。
总结:
1、无论 synchronized 关键字加在办法上还是对象上,如果它作用的对象是非动态的,则它获得的锁是对象;如果 synchronized 作用的对象是一个静态方法或一个类,则它获得的锁是对类,该类所有的对象同一把锁。
2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就能够运行它所管制的那段代码。
3、实现同步是要很大的零碎开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制