关于synchronized怎么理解,synchronized锁方法和锁代码块有什么区别?

时间:2021-11-22 13:00:54
有以下一个程序
public class TT extends Thread{
   int b=100;
   
   public synchronized void m1(){
      b=1000;
      try{
         Thread.sleep(5000);
      }catch(Exception e){
        e.printStackTrace();
      }
      System.out.println("b="+b);
   } 

   public synchronized void m2(){
     b=2000;
     try{
        Thread.sleep(2500);
     }catch(Exception e){
       e.printStackTrace();
     }
   }

   public void run(){
      try{
        m1();
      }catch(Exception e){
        e.printStackTrace();
      }   
   }

   public static void main(String args[]){
       TT tt = new TT();
       tt.start();
       tt.m2();
       System.out.println(tt.b);
  }
}
运行结果是:
2000
b=1000

如果把m2方法的synchronized关键字去掉的话运行结果就是:
1000
b=1000

为什么两次的结果不一致?

我想问的是这个程序中的synchronized方法到底锁的是什么东西?
要求强人对内存进行分析,希望不是三言两语就说完了。。
我自己的分析如下:
    首先执行主线程的main方法,在栈空间一个变量tt指向堆空间的一个TT对象。主线程的main方法调用tt.start()方法,启动了一个tt线程,这个线程进入可运行状态,但是可能占有CPU,也可能没有占有CPU.然后住线程调用tt.m2方法,把TT对象锁住了,b的值从100变成2000,然后Thread.sleep(2500),这个时候住线程就进入到睡眠状态,但是因为加了synchronized,所以tt线程得不到TT对象,所以只有等待主线程睡2.5秒自己醒。再往下执行,这个时候就打印出了2000这个值,同时将锁释放掉,这个时候tt线程拿到了锁,得到b的值,这时候b的值是2000,tt线程调用m1()方法,将b的值改成1000,然后tt线程睡眠5秒钟,将b=1000打印出来。不知道我的理解有哪些是不合理的地方?
    还有其中几点存在疑惑,1.tt.start()方法,是使一个线程进入可运行状态吗?但是这个线程还没有运行?还是说这个线程有可能获得CPU马上运行,还是在可运行池中等待运行呢?这个方法不是很理解。。。
    2.如果是实例变量,那么方法锁会把这个实例变量锁住吗,其他线程暂时就不能访问这个实力变量?我读synchronized方法就是这样理解的,不知道这样是不是正确的。。
     哪位高手帮我解答下啊,我被多线程搞糊涂了。。。感觉书上有时候也讲得不详细!!!  

13 个解决方案

#1


顶!牛人上之!接分

#2


有没有帮忙看一看啊!!

#3


33.5%

关于synchronized怎么理解,synchronized锁方法和锁代码块有什么区别?

#4


37.5% 的结帖率,只能帮顶!

#5


37.5% 的结帖率,只能帮顶! 关于synchronized怎么理解,synchronized锁方法和锁代码块有什么区别?

#6


。。。。我也不想这低啊,可是有的帖子不满意,有的帖子搞忘记回帖了。。。不好意思喔。。。

#7


额,要沉了,郁闷啊。。。没人给个解释吗。。。

#8


线程问题不容易表达清楚。我的U盘里找了一些可能适合你的信息。

调用start()方法之前:
处于NEW状态:理解为(初始化状态),线程对象已经被创建,但是还没有被启动时的状态。

调用start()方法之后:
处于RUNNABLE状态:理解为(可运行状态、就绪状态),在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。

synchronized:
线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

你要对内存进行分析估计真的要请牛人来了,JAVA线程不就是虚拟机线程吗,
虚拟机内存!太深了!


引用 4 楼 bao110908 的回复:
37.5% 的结帖率,只能帮顶!


为什么果子哥也顶啊!


#9


这两个帖子或许对你有些帮助

http://topic.csdn.net/u/20100515/11/fd2180ba-bd3d-4353-9a6f-a3c5de525417.html
http://topic.csdn.net/u/20100514/11/41d611ab-ce30-4d00-bb53-595f7f083650.html


引用 8 楼 cityjin 的回复:
引用 4 楼 bao110908 的回复:
37.5% 的结帖率,只能帮顶!

为什么果子哥也顶啊!


1:三无问题、问题描述不清不回
2:作业帖不回
3:结帖率太低不回

#10


jixudingxia ,buyao cengle 

#11


leisore的浅见:
1. 为什么两次执行结果不同:
   <1> main方法开始,主线程是运行
   <2> TestThread tt = new TestThread();
       tt.start();
       子线程开始。new一个线程是一个很繁重的任务,涉及到很多系统资源的分配和调度。这也是1.5中Doug Lea大牛加入了线程池的一个初中吧。至于是否可以立即运行,得看系统的分配。但是无论是否运行,主线程已经不是很关心了,那是线程调度器的事。主线程开始运行下一条语句。
    <3>tt.m2();
      进入同步的m2方法并把b值修改为2000,此后sleep,由于sleep是不释放锁的,所以即使sleep期间子线程醒来,他也不可以进入m1方法。
    <4>5秒后,m1继续,执行完毕,如果此时子线程还没有得到许可运行权,那么继续主线程,打印b值,为2000
    <5>子线程得到运行,修改b,并打印。

    如果把m2的同步标志去掉,那么在m2的sleep期间,m1开始运行,那么b的值是被m1修改了。 也就是说,synchronized标志只保证她所修饰的方法或包围的块,不允许多个线程同时访问

#12


马士兵的课堂代码,哈哈,你的答案应该搞反了。

#13


package com.test;

import java.util.concurrent.TimeUnit;

public class TT extends Thread {
  int b=100;
     
  public synchronized void m1(){
   b=1000;
   System.out.println("m1给b赋值");
   try{
   Thread.sleep(2000);
   System.out.println("m1睡醒");
   }catch(Exception e){
   e.printStackTrace();
   }
   System.out.println("m1中b="+b);
   }  

  public synchronized void m2(){
   b=2000;
   System.out.println("m2给b赋值");
   try{
   Thread.sleep(1000);
   System.out.println("m2睡醒");
   }catch(Exception e){
   e.printStackTrace();
   }
   System.out.println("m2中b="+b);
   }

   public void run(){
   try{
   m1();
   }catch(Exception e){
   e.printStackTrace();
   }   
  }

   public static void main(String args[]){
   TT tt = new TT();
   tt.start();
   try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
   System.out.println("m2等待锁");
   tt.m2();
   tt.b=3000;
   System.out.println("main中b="+tt.b);
   }
}


改造成这段代码,看打印输出你应该可以了解了。

#1


顶!牛人上之!接分

#2


有没有帮忙看一看啊!!

#3


33.5%

关于synchronized怎么理解,synchronized锁方法和锁代码块有什么区别?

#4


37.5% 的结帖率,只能帮顶!

#5


37.5% 的结帖率,只能帮顶! 关于synchronized怎么理解,synchronized锁方法和锁代码块有什么区别?

#6


。。。。我也不想这低啊,可是有的帖子不满意,有的帖子搞忘记回帖了。。。不好意思喔。。。

#7


额,要沉了,郁闷啊。。。没人给个解释吗。。。

#8


线程问题不容易表达清楚。我的U盘里找了一些可能适合你的信息。

调用start()方法之前:
处于NEW状态:理解为(初始化状态),线程对象已经被创建,但是还没有被启动时的状态。

调用start()方法之后:
处于RUNNABLE状态:理解为(可运行状态、就绪状态),在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。

synchronized:
线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

你要对内存进行分析估计真的要请牛人来了,JAVA线程不就是虚拟机线程吗,
虚拟机内存!太深了!


引用 4 楼 bao110908 的回复:
37.5% 的结帖率,只能帮顶!


为什么果子哥也顶啊!


#9


这两个帖子或许对你有些帮助

http://topic.csdn.net/u/20100515/11/fd2180ba-bd3d-4353-9a6f-a3c5de525417.html
http://topic.csdn.net/u/20100514/11/41d611ab-ce30-4d00-bb53-595f7f083650.html


引用 8 楼 cityjin 的回复:
引用 4 楼 bao110908 的回复:
37.5% 的结帖率,只能帮顶!

为什么果子哥也顶啊!


1:三无问题、问题描述不清不回
2:作业帖不回
3:结帖率太低不回

#10


jixudingxia ,buyao cengle 

#11


leisore的浅见:
1. 为什么两次执行结果不同:
   <1> main方法开始,主线程是运行
   <2> TestThread tt = new TestThread();
       tt.start();
       子线程开始。new一个线程是一个很繁重的任务,涉及到很多系统资源的分配和调度。这也是1.5中Doug Lea大牛加入了线程池的一个初中吧。至于是否可以立即运行,得看系统的分配。但是无论是否运行,主线程已经不是很关心了,那是线程调度器的事。主线程开始运行下一条语句。
    <3>tt.m2();
      进入同步的m2方法并把b值修改为2000,此后sleep,由于sleep是不释放锁的,所以即使sleep期间子线程醒来,他也不可以进入m1方法。
    <4>5秒后,m1继续,执行完毕,如果此时子线程还没有得到许可运行权,那么继续主线程,打印b值,为2000
    <5>子线程得到运行,修改b,并打印。

    如果把m2的同步标志去掉,那么在m2的sleep期间,m1开始运行,那么b的值是被m1修改了。 也就是说,synchronized标志只保证她所修饰的方法或包围的块,不允许多个线程同时访问

#12


马士兵的课堂代码,哈哈,你的答案应该搞反了。

#13


package com.test;

import java.util.concurrent.TimeUnit;

public class TT extends Thread {
  int b=100;
     
  public synchronized void m1(){
   b=1000;
   System.out.println("m1给b赋值");
   try{
   Thread.sleep(2000);
   System.out.println("m1睡醒");
   }catch(Exception e){
   e.printStackTrace();
   }
   System.out.println("m1中b="+b);
   }  

  public synchronized void m2(){
   b=2000;
   System.out.println("m2给b赋值");
   try{
   Thread.sleep(1000);
   System.out.println("m2睡醒");
   }catch(Exception e){
   e.printStackTrace();
   }
   System.out.println("m2中b="+b);
   }

   public void run(){
   try{
   m1();
   }catch(Exception e){
   e.printStackTrace();
   }   
  }

   public static void main(String args[]){
   TT tt = new TT();
   tt.start();
   try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
   System.out.println("m2等待锁");
   tt.m2();
   tt.b=3000;
   System.out.println("main中b="+tt.b);
   }
}


改造成这段代码,看打印输出你应该可以了解了。