Synchronized是Java的关键字,字面意思上是同步的意思,也就是说可以用它修饰普通方法、静态方法、代码块,达到同步的效果,就是我们俗称的上锁。
Synchronized作用域和作用对象? 使用Synchronized修饰普通方法使用Synchronized修饰普通方法,作用域是整个方法,作用对象是该类的当前对象,不同对象会持有不同的锁。下面我们来看看代码:
public class SynchronizedMethodTest {
synchronized public void printHello() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++){
System.out.println("Hello " + name);
Thread.sleep(1000);
}
}
synchronized public void printByeBye() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++){
System.out.println("Bye Bye " + name);
Thread.sleep(1000);
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedMethodTest s1 = new SynchronizedMethodTest();
new Thread(() -> {
System.out.println("Thread1 is start");
try {
s1.printHello();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
try {
s1.printByeBye();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread2 is end");
}).start();
}
}
SynchronizedMethodTest类有两个方法:printHello和prinByeBye都加了锁,主程序new了一个SynchronizedMethodTest对象,新建两个线程分别调用s1.printHello和s1.printByeBye方法。
程序执行结果如下:
Thread1 is start Hello Thread-0 Thread2 is start Hello Thread-0 Hello Thread-0 Hello Thread-0 Hello Thread-0 Thread1 is end Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Thread2 is end
可以发现Thread2要等Thread1执行完才执行,说明Thread1在执行printHello方法时拿到的是对象锁,即使Thread2调用的是不同方法,也需要等待。
为了证明这一点,我将代码稍作修改:
public class SynchronizedMethodTest {
synchronized public void printHello() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++){
System.out.println("Hello " + name);
Thread.sleep(1000);
}
}
synchronized public void printByeBye() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++){
System.out.println("Bye Bye " + name);
Thread.sleep(1000);
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedMethodTest s1 = new SynchronizedMethodTest();
SynchronizedMethodTest s2 = new SynchronizedMethodTest();
new Thread(() -> {
System.out.println("Thread1 is start");
try {
s1.printHello();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
try {
s2.printByeBye();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread2 is end");
}).start();
}
}
这次new了两个SynchronizedMethodTest对象,新建两个线程分别执行s1.printHello、s2.printByeBye方法。
执行结果如下:
Thread1 is start Hello Thread-0 Thread2 is start Bye Bye Thread-1 Hello Thread-0 Bye Bye Thread-1 Hello Thread-0 Bye Bye Thread-1 Hello Thread-0 Bye Bye Thread-1 Hello Thread-0 Bye Bye Thread-1 Thread1 is end Thread2 is end
从结果可以发现,Thread1和Thread2线程同时执行,因为这两个方法拿到的是两个不同对象的锁,所以互相不影响。
使用Synchronized修饰静态方法使用synchronized修饰静态方法,作用域是整个静态方法,作用对象是类,与类的对象无关。下面我们来看看代码:
public class SynchronizedClassTest {
synchronized public static void printHello() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Hello " + name);
Thread.sleep(1000);
}
}
synchronized public static void printByeBye() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Bye Bye " + name);
Thread.sleep(1000);
}
}
synchronized public void printGo() throws InterruptedException {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("GO!" + name);
Thread.sleep(1000);
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedClassTest s = new SynchronizedClassTest();
new Thread(() -> {
System.out.println("Thread1 is start");
try {
SynchronizedClassTest.printHello();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
try {
SynchronizedClassTest.printByeBye();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread2 is end");
}).start();
new Thread(() -> {
System.out.println("Thread3 is start");
try {
s.printGo();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread3 is end");
}).start();
}
}
这次我们有三个方法,其中printHello和printByeBye是静态方法,printGo是普通方法,新建三个线程分别调用这三个方法,结果如下:
Thread1 is start Hello Thread-0 Thread2 is start Thread3 is start GO!Thread-2 Hello Thread-0 GO!Thread-2 Hello Thread-0 GO!Thread-2 Hello Thread-0 GO!Thread-2 GO!Thread-2 Hello Thread-0 Thread3 is end Bye Bye Thread-1 Thread1 is end Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Thread2 is end
从结果可以发现Thread2要等待Thread1执行完再执行,说明对静态方法锁的是类,Thread3不受Thread1和Thread2的影响,说明对象锁和类锁不是一把锁,互不影响。
使用Synchronized修饰代码块使用synchronized修饰代码块,作用域是整个代码块里面的内容,作用对象是括号中的对象(类、对象),作用对象是类的时候,作用的是类及该类的所有对象,下面代码可以看到:
public class SynchronizedStaticBlockTest {
private void print(){
synchronized (SynchronizedStaticBlockTest.class){
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Hello " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedStaticBlockTest s1 = new SynchronizedStaticBlockTest();
SynchronizedStaticBlockTest s2 = new SynchronizedStaticBlockTest();
new Thread(() -> {
System.out.println("Thread1 is start");
s1.print();
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
s2.print();
System.out.println("Thread2 is end");
}).start();
}
}
有一个方法print,其中有一个被synchronized修饰的代码块,作用对象是SynchronizedStaticBlockTest,现在新建两个SynchronizedStaticBlockTest对象,新建两个线程分别调用s1.print和s2.print,结果如下:
Thread1 is start Hello Thread-0 Thread2 is start Hello Thread-0 Hello Thread-0 Hello Thread-0 Hello Thread-0 Thread1 is end Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Bye Bye Thread-1 Thread2 is end
可以发现,synchronized修饰代码块,作用对象为类的时候,作用的是这个类的所有对象。
我们现在稍微修改一下代码,新增一个print2方法,内部也有一个被synchronized修饰的代码块,但这次作用对象是this,也就是当前对象,现在新建两个线程执行这个方法。
public class SynchronizedStaticBlockTest {
public SynchronizedStaticBlockTest() {
}
private void print(){
synchronized (SynchronizedStaticBlockTest.class){
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Hello " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void print2(){
synchronized (this){
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Print 2 Hello " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedStaticBlockTest s1 = new SynchronizedStaticBlockTest();
SynchronizedStaticBlockTest s2 = new SynchronizedStaticBlockTest();
new Thread(() -> {
System.out.println("Thread1 is start");
s1.print2();
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
s2.print2();
System.out.println("Thread2 is end");
}).start();
}
}
结果如下:
Thread1 is start Print 2 Hello Thread-0 Thread2 is start Print 2 Hello Thread-1 Print 2 Hello Thread-0 Print 2 Hello Thread-1 Print 2 Hello Thread-0 Print 2 Hello Thread-1 Print 2 Hello Thread-0 Print 2 Hello Thread-1 Print 2 Hello Thread-0 Print 2 Hello Thread-1 Thread1 is end Thread2 is end
可以发现,Thread2没有等待Thread1执行完再执行,而是同时执行,说明当synchronized修饰代码块,作用对象是对象,那么只会锁当前对象。
为了方便大家记忆,我整理了一个表格来说明synchronized修饰普通方法、静态方法、代码块的作用域和作用对象。
| 修饰对象 | 作用域 | 作用对象 | 影响对象 |
|---|---|---|---|
| 普通方法 | 整个方法 | 对象 | 当前对象 |
| 静态方法 | 整个方法 | 类 | 当前类 |
| 代码块-类 | 整个代码块 | 类 | 当前类及该类所有对象 |
| 代码块-对象 | 整个代码块 | 对象 | 指定对象 |
下一篇,我们分析一下sychronized的底层原理。



