synchronized : 代表同步的修饰符
目录
1.特性:
- 互斥性(排他性) : 同一时刻,只允许一个线程进行访问,会一直等待线程释放锁,容易造成死锁。
- 重入性: 程序或者子程序可以在任意时刻被中断然后系统调度另一段代码,这段代码又调用了该子程序,并不会出错。 也 就是说,一个线程执行了某个程序,是可以再次进入该程序并执行。
内置锁:类借用synchronized是实现了一个内置性,我觉得更像是类的特性,只不过是类借用了这个修饰符来实现的内置 性。所有类都可以用自身对象创建一个内置锁。
2.互斥性demo:
一 . synchronized修饰普通方法:针对的是对象的某一实例。(并不影响对象的其他实例)
package com.test.concurrencyThread;
public class Test implements Runnable {
public synchronized void test() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
test();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Runnable runnable1 = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
程序输出:
Thread-0
Thread-1
(两者几乎同时输出)
package com.test.concurrencyThread;
public class Test implements Runnable {
public synchronized void test() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
test();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable);
thread1.start();
}
}
再看这第二个demo,由两个线程启动同一个Test的实例,输出的结果没变,只是在输出thread-0之后过了5秒,才输出Thread-1。
Thread-0
Thread-1
由此可见,synchronized修饰普通方法:针对的是对象的某一实例。(并不影响对象的其他实例)。
二. synchronized修饰静态方法:针对的是整个类。
package com.test.concurrencyThread;
public class Test implements Runnable {
public synchronized static void test1() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Runnable runnable1 = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
接下来synchronized修饰静态方法,无论是创建一个实例还是两个实例,都是先输出一个,之后过了5秒,再输出第二个。
Thread-0
Thread-1
由此可见,synchronized修饰静态方法:针对的是整个类。
三. synchronized修饰代码块:针对的是synchronized修饰的对象,(不影响synchronized修饰的其他对象)
package com.test.concurrencyThread;
public class Test implements Runnable {
private final Object object = new Object();
private void test1() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable);
thread1.start();
}
}
创建一个Test实例,由两个线程运行。结果也是先输出一个,过五秒之后,又输出第二个。
Thread-0
Thread-1
package com.test.concurrencyThread;
public class Test implements Runnable {
private final Object object = new Object();
private void test1() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Runnable runnable1 = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
Thread-0
Thread-1
结果几乎同时输出。由此可见,synchronized修饰代码块:针对的是synchronized修饰的对象,(不影响其他synchronized修饰的对象)。
3.重入性讲解
当一个线程请求一个由其他线程持有的对象锁时,该线程会阻塞。当线程请求自己持有的对象锁时,如果该线程是重入锁,请求就会成功,否则阻塞。
我们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。因此,在一个线程使用synchronized方法时调用该对象另一个synchronized方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的。
在Java内部,同一个线程调用自己类中其他synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程可以获取同一把锁多次,也就是可以多次重入。原因是Java中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。