黑马程序员技术交流社区

标题: 同步函数被静态修饰后,使用的锁是什么 [打印本页]

作者: 王敏NO.09    时间: 2012-4-25 20:42
标题: 同步函数被静态修饰后,使用的锁是什么
同步函数被静态修饰后,使用的锁是什么?
怎样判断锁是什么
作者: 魏征    时间: 2012-4-25 20:59
同步函数被静态修饰后 使用的锁是所在的类的字节码.class文件,没有被静态修饰的同步函数使用的锁是this。如果是同步代码块的锁是可以自定定义的,实现同步的条件是必须使用同一把锁。
作者: 刘基军    时间: 2012-4-25 21:27
  1. public class TestDemo implements Runnable {       

  2.         public static void main(String[] args) {
  3.                 TestDemo t = new TestDemo();
  4.                 new Thread(t).start();
  5.                 try{
  6.                         Thread.sleep(2000);
  7.                 }catch(Exception e) {
  8.                 }
  9.                 t.study();
  10.                
  11.         }
  12.        
  13.         @Override
  14.         public void run() {
  15.                 synchronized(this) {             //先不改程序运行,观看结果,然后再改成这样运行,比较一下结果: synchronized(TestDemo.class) ,你就该知道静态是哪个锁了
  16.                         while(true){
  17.                                 System.out.println("run...");
  18.                         }
  19.                 }
  20.         }
  21.        
  22.         public synchronized static void study() {
  23.                 while(true){
  24.                         try{
  25.                                 System.out.println("study...");
  26.                                 //Thread.sleep(2000);
  27.                         }catch(Exception e) {
  28.                         }
  29.                 }
  30.         }
  31. }
复制代码

作者: 袁冬梅    时间: 2012-4-25 22:04
               
如果同步函数被static修饰,使用的锁不是this,因为静态函数中不可以定义this。
静态函数进内存时,内存中并没有本类对象,但是一定有该类对应的字节码文件对象
——类名.class ,该对象的类型是class。静态同步方法使用的锁就是该方法所在的字节码文件对象,即:类名.class。

这时候的这个类名.class 文件其实是Class类的一个实例对象,也就是说它也是一个对象,可以用来做锁的。

希望能帮到你,O(∩_∩)O~
作者: 小鹿叙鹿    时间: 2012-4-25 22:58
首先静态函数是随着类i的加载而加载,它不可能用的是this,因为this需要对象,而对象是通过类创建的,所以是类.class即  .class文件被加载到内存中的字节码文件
但是不是静态修饰的函数默认用的是this,这可以通过实验得出。而同步代码块用的锁,可以是this,也可以是字节码文件运行时的类,亦可以自己来定义一个锁,
但是必须保证所有操作共享数据的线程用的是同一把锁。
作者: 袁錦泰    时间: 2012-4-25 23:35
小鹿叙鹿 发表于 2012-4-25 22:58
首先静态函数是随着类i的加载而加载,它不可能用的是this,因为this需要对象,而对象是通过类创建的,所以 ...


能否演示一下實驗得出結果的過程?
作者: 袁冬梅    时间: 2012-4-26 04:11
- -看到你的问题突然发现我也不太记得这个的原理了,于是就自己动手开始写代码了。
整理了一下,总算磕磕绊绊写出来了。

程序的思路:
                创建一个同步代码块和一个静态同步函数。两边同步的内容都是共享数据num
                设置一个flag标志,以保证我们修改了flag之后,线程会进入静态同步函数中运行

这个程序中主要的部分是        synchronized(StaticLock.class)//锁。这里面的这个锁。
我设定了三个锁来实验
静态同步函数所在类的字节码对象:StaticLock.class
主函数所在类的字节码对象:TestStaticLock.class
同步代码块所属的本类的本类对象this

最后得出的答案是,后两者都出现了线程安全问题。其中一个线程打印出了0.

咳咳,我大约就理解到这样了,如果理解有误还请多提点提点,初学者很多都不懂。OTZ
代码如下:
  1. class StaticLock implements Runnable
  2. {
  3.         private static int num = 100;
  4.         private boolean flag = true;
  5.         private boolean flags = true;
  6.         public void run()
  7.         {
  8.                 if(flag)
  9.                 {
  10.                         while(flags)
  11.                         {
  12.                                 synchronized(StaticLock.class)//锁。
  13.                                 {
  14.                                         if(num>0)
  15.                                         {
  16.                                                 try{Thread.sleep(10);}catch(Exception e){}//让线程在这里睡一下,让问题出现的更明显一点,如果不是安全的锁,那么最后会有其中一个线程打印出num的值为0来
  17.                                                 System.out.println(Thread.currentThread().getName()+"…………走正常锁……"+num--);       
  18.                                         }                                                                       
  19.                                 }
  20.                         }
  21.                 }
  22.                 else
  23.                 {
  24.                         while(flags)//这里调用静态同步函数
  25.                         {
  26.                                 show();
  27.                         }
  28.                 }               
  29.         }
  30.         public static synchronized void show()//静态同步函数
  31.         {
  32.                 if(num>0)
  33.                 {
  34.                         try{Thread.sleep(10);}catch(Exception e){}//让线程在这里睡一下,让问题出现的更明显一点,如果不是安全的锁,那么最后会有其中一个线程打印出num的值为0来
  35.                         System.out.println(Thread.currentThread().getName()+"…………走静态同步代码块……"+num--);               
  36.                 }               
  37.         }
  38.         public void setFlag()
  39.         {
  40.                 this.flag = false;
  41.         }
  42.         public void setFlags()
  43.         {
  44.                 this.flags = false;
  45.         }
  46. }
  47. class TestStaticLock
  48. {
  49.         public static void main(String[] args)throws Exception
  50.         {
  51.                 StaticLock stl = new StaticLock();
  52.                 Thread t1 = new Thread(stl);
  53.                 Thread t2 = new Thread(stl);
  54.                 t1.start();
  55.                 Thread.sleep(10);
  56.                 stl.setFlag();//这里设置这个标志位是为了让t2走静态同步函数里面的代码块
  57.                 t2.start();
  58.                 Thread.sleep(4000);//让主线程睡4S,等t1和t2执行完里面的数据一下
  59.                 int x = 100;
  60.                 while(x>0)
  61.                 {
  62.                         x--;
  63.                         if(x == 50)
  64.                                 stl.setFlags();//设置t1和t2停止循环的标志位
  65.                 }                               
  66.         }
  67. }
复制代码

作者: 袁冬梅    时间: 2012-4-26 04:14
袁錦泰 发表于 2012-4-25 23:35
能否演示一下實驗得出結果的過程?

o(╯□╰)o,大半夜的脑子不灵光了,发出来才发现回复的不是你。
你来看代码吧。。
基本应该可以理解静态同步函数的锁的是哪个这个问题了。
另外,我也是新手,没什么java基础的,你愿意和我做朋友,我自然很开心,O(∩_∩)O~,已经加你好友了。
作者: 毕博    时间: 2012-4-26 05:17


    当多条语句在操作同一个线程的共享数据的时候,一个线程对多条语句只执行了一部分,还没有执行完另一个线程参与进来导致共享数据的错误。

    解决办法:

    对多条操作数据的语句只能让一个线程执行完其他线程不可参与执行,Java对于多线程的安全问题提供了专业的解决办法就是同步代码块和同步函数

   同步代码块的格式:

    synchronized(对象) {

      需要被同步的代码;

                         }



   对象如同锁,持有锁的线程可以在同步中执行

   没有持有锁的线程即使在获取cpu的执行权也进不去因为没有锁。

   同步的前提:

       1.必须要有两个或者两个以上的线程

       2.必须是多个线程使用同一个锁

   同步的好处:

       解决了多线程的安全问题

   同步的弊端:

       多线程需要判断锁,较为耗费资源。

   同步函数用的是那一个锁呢?

   函数需要被对象调用,那么函数都有一个所属对象引用就是this,所以同步函数的使用的锁是this

   如果同步函数被静态修饰后使用的锁是什么呢?

   通过验证发现不再是this,因为静态方法中也不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象类名.class该对象的类型是Class。所以静态同步方法使用的锁是该方法所在类的字节码文件对象,类名.class。



   synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
   1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
  public synchronized void accessVal(int newVal);
  synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能

执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行

状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有

一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)


在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成

员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为

synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可

以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供

了更好的解决办法,那就是 synchronized 块。


  



作者: 袁錦泰    时间: 2012-4-27 00:25
袁冬梅 发表于 2012-4-26 04:14
o(╯□╰)o,大半夜的脑子不灵光了,发出来才发现回复的不是你。
你来看代码吧。。
基本应该可以理解静态 ...

:handshake
作者: 孙天    时间: 2012-4-27 10:51
同步函数使用的锁:this
静态同步函数使用的锁:类名.class,是静态函数所属类的字节码文件对象




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2