1.两个线程调用同一个对象的两个同步方法

  1. package thread;
  2. public class ThreadTest {
  3. //两个线程调用同一个对象的两个同步方法
  4. public static void main(String[] args) {
  5. Number number = new Number();
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. number.getOne();
  10. }
  11. }).start();
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. number.getTwo();
  16. }
  17. }).start();
  18. }
  19. }
  20. class Number{
  21. public synchronized void getOne(){
  22. System.out.println("one");
  23. }
  24. public synchronized void getTwo(){
  25. System.out.println("two");
  26. }
  27. }

结果:
1.png
结论:被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行。

2.新增Thread.sleep()给某个方法

  1. package thread;
  2. public class ThreadTest {
  3. //新增Thread.sleep()给getOne方法
  4. public static void main(String[] args) {
  5. Number number = new Number();
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. number.getOne();
  10. }
  11. }).start();
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. number.getTwo();
  16. }
  17. }).start();
  18. }
  19. }
  20. class Number{
  21. public synchronized void getOne(){
  22. try{
  23. Thread.sleep(1000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("one");
  28. }
  29. public synchronized void getTwo(){
  30. System.out.println("two");
  31. }
  32. }

结果:
2.png
结论:被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行,第二个方法只有在第一个方法执行完释放锁之后才能执行。

3.新增一个线程调用新增的一个普通方法

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number = new Number();
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. number.getOne();
  9. }
  10. }).start();
  11. new Thread(new Runnable() {
  12. @Override
  13. public void run() {
  14. number.getTwo();
  15. }
  16. }).start();
  17. new Thread(new Runnable() {
  18. @Override
  19. public void run() {
  20. number.getThree();
  21. }
  22. }).start();
  23. }
  24. }
  25. class Number{
  26. public synchronized void getOne(){
  27. try{
  28. Thread.sleep(1000);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println("one");
  33. }
  34. public synchronized void getTwo(){
  35. System.out.println("two");
  36. }
  37. public void getThree(){
  38. System.out.println("three");
  39. }
  40. }

结果:
3.png
结论:新增的方法没有被synchronized修饰,不是同步方法,不受锁的影响,所以不需要等待。其他线程共用了一把锁,所以还需要等待。

4.两个线程调用两个对象的同步方法,其中一个方法有Thread.sleep()

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number1 = new Number();
  5. Number number2 = new Number();
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. number1.getOne();
  10. }
  11. }).start();
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. number2.getTwo();
  16. }
  17. }).start();
  18. }
  19. }
  20. class Number{
  21. public synchronized void getOne(){
  22. try{
  23. Thread.sleep(1000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("one");
  28. }
  29. public synchronized void getTwo(){
  30. System.out.println("two");
  31. }
  32. }

结果:
4.png
结论:被synchronized修饰的方法,锁的对象是方法的调用者。因为用了两个对象调用各自的方法,所以两个方法的调用者不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

5.将有Thread.sleep()的方法设置为static方法,并且让两个线程用同一个对象调用两个方法

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number = new Number();
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. number.getOne();
  9. }
  10. }).start();
  11. new Thread(new Runnable() {
  12. @Override
  13. public void run() {
  14. number.getTwo();
  15. }
  16. }).start();
  17. }
  18. }
  19. class Number{
  20. public static synchronized void getOne(){
  21. try{
  22. Thread.sleep(1000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. System.out.println("one");
  27. }
  28. public synchronized void getTwo(){
  29. System.out.println("two");
  30. }
  31. }

结果:
5.png
结论:被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

6.将两个方法均设置为static方法,并且让两个线程用同一个对象调用两个方法

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number = new Number();
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. number.getOne();
  9. }
  10. }).start();
  11. new Thread(new Runnable() {
  12. @Override
  13. public void run() {
  14. number.getTwo();
  15. }
  16. }).start();
  17. }
  18. }
  19. class Number{
  20. public static synchronized void getOne(){
  21. try{
  22. Thread.sleep(1000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. System.out.println("one");
  27. }
  28. public static synchronized void getTwo(){
  29. System.out.println("two");
  30. }
  31. }

结果:
6.png
结论:被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,所以两个方法用的是同一个锁,后调用的方法需要等待先调用的方法。

7.将两个方法中有Thread.sleep()的方法设置为static方法,另一个方法去掉static修饰,让两个线程用两个对象调用两个方法

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number1 = new Number();
  5. Number number2 = new Number();
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. number1.getOne();
  10. }
  11. }).start();
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. number2.getTwo();
  16. }
  17. }).start();
  18. }
  19. }
  20. class Number{
  21. public static synchronized void getOne(){
  22. try{
  23. Thread.sleep(1000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("one");
  28. }
  29. public synchronized void getTwo(){
  30. System.out.println("two");
  31. }
  32. }

结果:
7.png
结论:被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。即便是用同一个对象调用两个方法,锁的对象也不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

8.将两个方法均设置为static方法,并且让两个线程用同一个对象调用两个方法

  1. package thread;
  2. public class ThreadTest {
  3. public static void main(String[] args) {
  4. Number number1 = new Number();
  5. Number number2 = new Number();
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. number1.getOne();
  10. }
  11. }).start();
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. number2.getTwo();
  16. }
  17. }).start();
  18. }
  19. }
  20. class Number{
  21. public static synchronized void getOne(){
  22. try{
  23. Thread.sleep(1000);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("one");
  28. }
  29. public static synchronized void getTwo(){
  30. System.out.println("two");
  31. }
  32. }

结果:
8.png
结论:被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,即便用了两个不同的对象调用方法,两个方法用的还是同一个锁,后调用的方法需要等待先调用的方法。

总结

  1. 一个类中如果有多个synchronized方法,使用一个对象调用的前提下,只要有一个线程去调用其中的一个syn方法,其它线程只能等待(使用的都是同一把锁)
  2. 普通方法与syn方法无关
  3. 静态同步方法锁的是整个class,而syn方法锁的是方法调用者(详见第五种情况)两把锁不一样,所以互不影响
  4. 所有的非静态同步方法用的都是同一把锁:实例对象本身。
  5. 所有的静态同步方法用的也是同一把锁:类对象本身。