1. 两个线程同时访问一个对象的同步方法
    这种情况,他们会一个一个执行

    1. package top.bowen.controller;
    2. public class SynchronizedObjectMethod implements Runnable {
    3. @Override
    4. public void run() {
    5. method();
    6. }
    7. //方法加了synchronized关键字,如果不加,将会同时执行
    8. public synchronized void method(){
    9. System.out.println("我是对象锁的方法修饰符 "+ Thread.currentThread().getName());
    10. try {
    11. Thread.sleep(2000);
    12. } catch (InterruptedException e) {
    13. e.printStackTrace();
    14. }
    15. System.out.println(Thread.currentThread().getName() + "运行结束");
    16. }
    17. public static void main(String[] args) {
    18. SynchronizedObjectMethod instance = new SynchronizedObjectMethod();
    19. Thread t1 = new Thread(instance);
    20. Thread t2 = new Thread(instance);
    21. t1.start();
    22. t2.start();
    23. }
    24. }

    运行结果:

    1. 我是对象锁的方法修饰符 Thread-0
    2. Thread-0运行结束
    3. 我是对象锁的方法修饰符 Thread-1
    4. Thread-1运行结束

    PS:
    Void类是用final修饰的,说明不可以扩展,另外构造方法是私有的,不可以实例化;
    Void类是一个不可实例化的占位符类,用来保存一个引用代表了Java关键字void的Class对象。
    public synchronized void method是同一个对象的void方法,那么他指代的是同一个类

  2. 两个线程访问两个对象的同步方法
    两个线程会同时执行,因为他们相当于是两个不同的实例

    1. package top.bowen.controller;
    2. public class synchronizedCodeBlock implements Runnable {
    3. @Override
    4. public void run() {
    5. //使用的是this,但是是新建了两个不同的实例,所以不是同一个锁
    6. synchronized (this){
    7. System.out.println("我是对象锁的同步代码块 "+ Thread.currentThread().getName());
    8. try {
    9. Thread.sleep(2000);
    10. } catch (InterruptedException e) {
    11. e.printStackTrace();
    12. }
    13. System.out.println(Thread.currentThread().getName() + "运行结束");
    14. }
    15. }
    16. public static void main(String[] args) {
    17. synchronizedCodeBlock instance1 = new synchronizedCodeBlock();
    18. synchronizedCodeBlock instance2 = new synchronizedCodeBlock();
    19. Thread t1 = new Thread(instance1);
    20. Thread t2 = new Thread(instance2);
    21. t1.start();
    22. t2.start();
    23. }
    24. }

    运行结果:

    1. 我是对象锁的同步代码块 Thread-0
    2. 我是对象锁的同步代码块 Thread-1
    3. Thread-1运行结束
    4. Thread-0运行结束
  3. 两个线程访问的是synchronized的静态方法
    他们一个一个执行,不同的实例,但是方法是静态的,那么对应的锁就是同一把,所以他们会一个一个执行

    1. package top.bowen.controller;
    2. public class synchronizedStaticMethod implements Runnable {
    3. @Override
    4. public void run() {
    5. method();
    6. }
    7. public static synchronized void method(){
    8. System.out.println("我是对象锁的静态方法 "+ Thread.currentThread().getName());
    9. try {
    10. Thread.sleep(2000);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. System.out.println(Thread.currentThread().getName() + "运行结束");
    15. }
    16. public static void main(String[] args) {
    17. synchronizedStaticMethod instance1 = new synchronizedStaticMethod();
    18. synchronizedStaticMethod instance2 = new synchronizedStaticMethod();
    19. Thread t1 = new Thread(instance1);
    20. Thread t2 = new Thread(instance2);
    21. t1.start();
    22. t2.start();
    23. }
    24. }

    运行结果:

    1. 我是对象锁的静态方法 Thread-0
    2. Thread-0运行结束
    3. 我是对象锁的静态方法 Thread-1
    4. Thread-1运行结束
  4. 同时访问同步方法和非同步方法
    非同步方法不受到同步方法的影响,

    1. package top.bowen.controller;
    2. /**
    3. * 多线程同时访问同步方法和非同步方法
    4. */
    5. public class SynchronizedYesOrNo implements Runnable{
    6. @Override
    7. public void run() {
    8. if(Thread.currentThread().getName().equals("Thread-0")){
    9. method1();
    10. }else{
    11. method2();
    12. }
    13. }
    14. public synchronized void method1(){
    15. System.out.println("我是加锁方法 "+ Thread.currentThread().getName());
    16. try {
    17. Thread.sleep(2000);
    18. } catch (InterruptedException e) {
    19. e.printStackTrace();
    20. }
    21. System.out.println(Thread.currentThread().getName() + "运行结束");
    22. }
    23. public void method2(){
    24. System.out.println("我是不加锁的方法 "+ Thread.currentThread().getName());
    25. try {
    26. Thread.sleep(2000);
    27. } catch (InterruptedException e) {
    28. e.printStackTrace();
    29. }
    30. System.out.println(Thread.currentThread().getName() + "运行结束");
    31. }
    32. public static void main(String[] args) {
    33. SynchronizedYesOrNo instance1 = new SynchronizedYesOrNo();
    34. SynchronizedYesOrNo instance2 = new SynchronizedYesOrNo();
    35. Thread t1 = new Thread(instance1);
    36. Thread t2 = new Thread(instance2);
    37. t1.start();
    38. t2.start();
    39. }
    40. }

    运行结果:

    1. 我是不加锁的方法 Thread-1
    2. 我是加锁方法 Thread-0
    3. Thread-0运行结束
    4. Thread-1运行结束
  5. 访问同一个对象不同的普通同步方法
    同一个对象是一个实例,那么两个方法需要一个一个执行

    1. package top.bowen.controller;
    2. public class SynchronizedTwoMethod implements Runnable{
    3. @Override
    4. public void run() {
    5. if(Thread.currentThread().getName().equals("Thread-0")){
    6. method1();
    7. }else{
    8. method2();
    9. }
    10. }
    11. public synchronized void method1(){
    12. System.out.println("我是method1 "+ Thread.currentThread().getName());
    13. try {
    14. Thread.sleep(2000);
    15. } catch (InterruptedException e) {
    16. e.printStackTrace();
    17. }
    18. System.out.println(Thread.currentThread().getName() + "运行结束");
    19. }
    20. public synchronized void method2(){
    21. System.out.println("我是method2 "+ Thread.currentThread().getName());
    22. try {
    23. Thread.sleep(2000);
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. System.out.println(Thread.currentThread().getName() + "运行结束");
    28. }
    29. public static void main(String[] args) {
    30. SynchronizedTwoMethod instance1 = new SynchronizedTwoMethod();
    31. // SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
    32. Thread t1 = new Thread(instance1);
    33. Thread t2 = new Thread(instance1);
    34. t1.start();
    35. t2.start();
    36. }
    37. }

    运行结果:

    1. 我是method1 Thread-0
    2. Thread-0运行结束
    3. 我是method2 Thread-1
    4. Thread-1运行结束
  6. 静态方法锁和非静态方法锁
    他们想当于是两把不同的锁,void method2()背后是this;static synchronized void method1背后是.class对象,他们两个对象不一样,所以会同步执行

    1. package top.bowen.controller;
    2. public class SynchronizedstaticAndNom implements Runnable{
    3. @Override
    4. public void run() {
    5. if(Thread.currentThread().getName().equals("Thread-0")){
    6. method1();
    7. }else{
    8. method2();
    9. }
    10. }
    11. public static synchronized void method1(){
    12. System.out.println("我是静态方法method1 "+ Thread.currentThread().getName());
    13. try {
    14. Thread.sleep(2000);
    15. } catch (InterruptedException e) {
    16. e.printStackTrace();
    17. }
    18. System.out.println(Thread.currentThread().getName() + "运行结束");
    19. }
    20. public synchronized void method2(){
    21. System.out.println("我是非静态方法method2 "+ Thread.currentThread().getName());
    22. try {
    23. Thread.sleep(2000);
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. System.out.println(Thread.currentThread().getName() + "运行结束");
    28. }
    29. public static void main(String[] args) {
    30. SynchronizedstaticAndNom instance1 = new SynchronizedstaticAndNom();
    31. // SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
    32. Thread t1 = new Thread(instance1);
    33. Thread t2 = new Thread(instance1);
    34. t1.start();
    35. t2.start();
    36. }
    37. }

    运行结果:

    1. 我是非静态方法method2 Thread-1
    2. 我是静态方法method1 Thread-0
    3. Thread-1运行结束
    4. Thread-0运行结束

    7.方法抛出异常后,会释放锁
    抛出异常后,java会自动帮我们释放锁

    1. package top.bowen.controller;
    2. /**
    3. * 方法抛出异常时,锁是否会被释放
    4. * 展示不抛出异常和抛出异常后
    5. * 一旦抛出异常,第二个线程会立即进入同步方法,说明锁被释放了
    6. *
    7. */
    8. public class SynchronizedException implements Runnable {
    9. @Override
    10. public void run() {
    11. if(Thread.currentThread().getName().equals("Thread-0")){
    12. method1();
    13. }else{
    14. method2();
    15. }
    16. }
    17. public synchronized void method1(){
    18. System.out.println("我是method1 "+ Thread.currentThread().getName());
    19. try {
    20. Thread.sleep(2000);
    21. } catch (InterruptedException e) {
    22. e.printStackTrace();
    23. }
    24. //抛出异常
    25. throw new RuntimeException();
    26. }
    27. public synchronized void method2(){
    28. System.out.println("我是method2 "+ Thread.currentThread().getName());
    29. try {
    30. Thread.sleep(2000);
    31. } catch (InterruptedException e) {
    32. e.printStackTrace();
    33. }
    34. System.out.println(Thread.currentThread().getName() + "运行结束");
    35. }
    36. public static void main(String[] args) {
    37. SynchronizedException instance1 = new SynchronizedException();
    38. // SynchronizedTwoMethod instance2 = new SynchronizedTwoMethod();
    39. Thread t1 = new Thread(instance1);
    40. Thread t2 = new Thread(instance1);
    41. t1.start();
    42. t2.start();
    43. }
    44. }

    运行结果:

    1. 我是method1 Thread-0
    2. 我是method2 Thread-1
    3. Exception in thread "Thread-0" java.lang.RuntimeException
    4. at top.bowen.controller.SynchronizedException.method1(SynchronizedException.java:28)
    5. at top.bowen.controller.SynchronizedException.run(SynchronizedException.java:13)
    6. at java.base/java.lang.Thread.run(Thread.java:834)
    7. Thread-1运行结束
    8. Process finished with exit code 0

    总结

  • 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应1、5种情况)
  • 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是.class(synchronized (.class){}这种类型)以及synchronized修饰的是static时。所有对象共用同一把锁(对应2、3、4、6这几种情况)
  • 无论方法是正常执行完毕,还是抛出异常。都会释放锁(对应第7种情况)
  • 使用synchronized注意点:锁对象不能为空、作用域不能过大,避难死锁。

参考 慕课网课程 Java高并发之魂:synchronized深度解析