两个线程同时访问一个对象的同步方法
这种情况,他们会一个一个执行package top.bowen.controller;
public class SynchronizedObjectMethod implements Runnable {
@Override
public void run() {
method();
}
//方法加了synchronized关键字,如果不加,将会同时执行
public synchronized void method(){
System.out.println("我是对象锁的方法修饰符 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
SynchronizedObjectMethod instance = new SynchronizedObjectMethod();
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
}
}
运行结果:
我是对象锁的方法修饰符 Thread-0
Thread-0运行结束
我是对象锁的方法修饰符 Thread-1
Thread-1运行结束
PS:
Void类是用final修饰的,说明不可以扩展,另外构造方法是私有的,不可以实例化;
Void类是一个不可实例化的占位符类,用来保存一个引用代表了Java关键字void的Class对象。
public synchronized void method是同一个对象的void方法,那么他指代的是同一个类两个线程访问两个对象的同步方法
两个线程会同时执行,因为他们相当于是两个不同的实例package top.bowen.controller;
public class synchronizedCodeBlock implements Runnable {
@Override
public void run() {
//使用的是this,但是是新建了两个不同的实例,所以不是同一个锁
synchronized (this){
System.out.println("我是对象锁的同步代码块 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
public static void main(String[] args) {
synchronizedCodeBlock instance1 = new synchronizedCodeBlock();
synchronizedCodeBlock instance2 = new synchronizedCodeBlock();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
}
}
运行结果:
我是对象锁的同步代码块 Thread-0
我是对象锁的同步代码块 Thread-1
Thread-1运行结束
Thread-0运行结束
两个线程访问的是synchronized的静态方法
他们一个一个执行,不同的实例,但是方法是静态的,那么对应的锁就是同一把,所以他们会一个一个执行package top.bowen.controller;
public class synchronizedStaticMethod implements Runnable {
@Override
public void run() {
method();
}
public static synchronized void method(){
System.out.println("我是对象锁的静态方法 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
synchronizedStaticMethod instance1 = new synchronizedStaticMethod();
synchronizedStaticMethod instance2 = new synchronizedStaticMethod();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
}
}
运行结果:
我是对象锁的静态方法 Thread-0
Thread-0运行结束
我是对象锁的静态方法 Thread-1
Thread-1运行结束
同时访问同步方法和非同步方法
非同步方法不受到同步方法的影响,package top.bowen.controller;
/**
* 多线程同时访问同步方法和非同步方法
*/
public class SynchronizedYesOrNo implements Runnable{
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
public synchronized void method1(){
System.out.println("我是加锁方法 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public void method2(){
System.out.println("我是不加锁的方法 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
SynchronizedYesOrNo instance1 = new SynchronizedYesOrNo();
SynchronizedYesOrNo instance2 = new SynchronizedYesOrNo();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
}
}
运行结果:
我是不加锁的方法 Thread-1
我是加锁方法 Thread-0
Thread-0运行结束
Thread-1运行结束
访问同一个对象不同的普通同步方法
同一个对象是一个实例,那么两个方法需要一个一个执行package top.bowen.controller;
public class SynchronizedTwoMethod implements Runnable{
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
public synchronized void method1(){
System.out.println("我是method1 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public synchronized void method2(){
System.out.println("我是method2 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
SynchronizedTwoMethod instance1 = new SynchronizedTwoMethod();
// SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance1);
t1.start();
t2.start();
}
}
运行结果:
我是method1 Thread-0
Thread-0运行结束
我是method2 Thread-1
Thread-1运行结束
静态方法锁和非静态方法锁
他们想当于是两把不同的锁,void method2()背后是this;static synchronized void method1背后是.class对象,他们两个对象不一样,所以会同步执行package top.bowen.controller;
public class SynchronizedstaticAndNom implements Runnable{
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
public static synchronized void method1(){
System.out.println("我是静态方法method1 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public synchronized void method2(){
System.out.println("我是非静态方法method2 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
SynchronizedstaticAndNom instance1 = new SynchronizedstaticAndNom();
// SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance1);
t1.start();
t2.start();
}
}
运行结果:
我是非静态方法method2 Thread-1
我是静态方法method1 Thread-0
Thread-1运行结束
Thread-0运行结束
7.方法抛出异常后,会释放锁
抛出异常后,java会自动帮我们释放锁package top.bowen.controller;
/**
* 方法抛出异常时,锁是否会被释放
* 展示不抛出异常和抛出异常后
* 一旦抛出异常,第二个线程会立即进入同步方法,说明锁被释放了
*
*/
public class SynchronizedException implements Runnable {
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
public synchronized void method1(){
System.out.println("我是method1 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//抛出异常
throw new RuntimeException();
}
public synchronized void method2(){
System.out.println("我是method2 "+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
SynchronizedException instance1 = new SynchronizedException();
// SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance1);
t1.start();
t2.start();
}
}
运行结果:
我是method1 Thread-0
我是method2 Thread-1
Exception in thread "Thread-0" java.lang.RuntimeException
at top.bowen.controller.SynchronizedException.method1(SynchronizedException.java:28)
at top.bowen.controller.SynchronizedException.run(SynchronizedException.java:13)
at java.base/java.lang.Thread.run(Thread.java:834)
Thread-1运行结束
Process finished with exit code 0
总结
- 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应1、5种情况)
- 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是.class(synchronized (.class){}这种类型)以及synchronized修饰的是static时。所有对象共用同一把锁(对应2、3、4、6这几种情况)
- 无论方法是正常执行完毕,还是抛出异常。都会释放锁(对应第7种情况)
- 使用synchronized注意点:锁对象不能为空、作用域不能过大,避难死锁。
参考 慕课网课程 Java高并发之魂:synchronized深度解析