A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张凯 中级黑马   /  2012-7-17 20:24  /  2211 人查看  /  8 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 张凯 于 2012-7-19 20:02 编辑

代码如下:

package com;

public class method {
private static method m = null;

private static byte[] b = new byte[0];
private method(){

}

public static method getInstance(){
if(m==null){
m = new method();
}
return m;
}

public   void print1(){
synchronized (b) {
for(int i = 0;i<1220;i++){
                   System.out.println(Thread.currentThread().getName()+i);
}
}

}

public   void print2(){
synchronized (b) {
for(int i = 0;i<1220;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}

}



package com;

public class test {

public static void main(String[] args) {
test t=new test();
t.init();
}

public void init(){
Runnable r = new thread1();
Thread t = new Thread(r,"A");
t.start();
Runnable r1 = new thread2();
Thread t1 = new Thread(r,"B");
t1.start();
}
public class thread1 implements Runnable{
public void run() {
method m = method.getInstance();
m.print1();

}

}

public class thread2 implements Runnable{
public void run() {
method m = method.getInstance();
m.print2();

}

}

}
问题:
如若synchronized (b)中的b是static 则执行的结果是AB按顺序出现,若b是非static则AB交替出现,若b换成thisAB也是交替出现。谁能给我解释下为什么吗,method类中的两个方法里的锁不是指向同一个对象吗。谢谢 。。。。

评分

参与人数 1技术分 +1 收起 理由
蒋映辉 + 1

查看全部评分

8 个回复

倒序浏览
由于,类method采用单例模式,故在整个程序运行过程中,只有一个method实例在运行,故用this可以实现同步互斥效果。尽管其采用单例模式,但其中的成员b,在测试类test中,被初始化两次,若b是类成员,虽然初始化两次,但是同一个对象,实现了同步互斥的效果。若b前不加static修饰,则会初始化两次,会产生两个不同的对象,达不到互斥的效果。
回复 使用道具 举报
首先,我们应该养成良好的命名习惯和代码的格式、
        Java中的名称规范
                见名知意:看见名字知道意思
                驼峰命名
                A:包 其实就是文件夹
                   **全部小写 xxx.yyy.zzz
                   **www.baidu.com com.baidu.www
                   **www.itcast.cn cn.itcast.www
                B:类或者接口
                   **如果由一个单词组成,首字母大写。例如 Demo
                   **如果由多个单词组成,每个单词的数字母大写。例如 HelloWorld
                C:变量和函数
                   **如果由一个单词组成,全部小写。例如 name
                   **如果由多个单词组成,第一个单词的数字母小写,其后每个单词的数字母大写。例如 getAge
                   **变量名是名称,函数名是动词+名称
                D:
                   **如果由一个单词组成,全部大写。例如 MONEY
                   **如果由多个单词组成,每个单词大学,然后用_连接。例如 BANK_MONEY
其次:你那个test类中的Runnable r1 = new thread2(); 这句话是没有用到。r1用不到,所有thread2()方法也是用不到的,是多余的,接着你可以看出print2()方法也是多余的,根本没有用到。不信你可以删除试试。
最后就是锁的问题。如果非静态的b,随着两个线程的启动,成员变量b会初始化两次。说明他的锁不一样的,多个线程使用不一样的锁就产生不了同步的作用。静态和this话,虽然测试类初始化两次,但是他是单例设计模式,this用的是同一个锁,静态成员变量只会初始化一次。所有用的也是同一个锁。同一个锁就会产生同步的效果,等当前线程执行完这个for循环后第二个线程才能进去这个代码块。所有就会出现这个按顺序出现。

回复 使用道具 举报
首先,在我的机器上运行并没有出现你所说的情况,三种写法都是AB按顺序打印
其次,这段代码违背了同步的一个前提:必须有两个或两个以上的线程;
这边看似创建了两个线程,实则是两个独立的线程操作各自的方法,互不干扰。就拿thread1来说,只给它创建了一个线程t去执行print1方法;那么加不加锁都是一样的效果。
最后,print1和print2虽然操作的是同一资源:method的实例对象,但是它们是被独立的线程执行的,就按照main方法创建线程的顺序依次输出

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
本帖最后由 陈淑飞 于 2012-7-18 09:06 编辑
柯玲 发表于 2012-7-17 23:55
首先,在我的机器上运行并没有出现你所说的情况,三种写法都是AB按顺序打印
其次,这段代码违背了同步的一 ...

print1和print2虽然操作的是同一资源:method的实例对象


不太认可这句。 其实,b中加不加static很关键。
当加static后 b就成了静态成员变量,所有实例共享,只有一份。此时,对于这两个线程来说,就是加了同一把锁,那么 结果只能是  谁先抢到锁,谁就先跑完,另一个再跑,所以A跑完后,B再跑。

当static 不修饰b 时,b 就是成员变量了,这里同步块的中b,是否是同一锁,那就要看method m是否是同一对象了。 显然,这里他虽然用了“单例模式”,但是他用的,只适合单线程时,得到的是同一对象。而多线程中,可能就是两个method对象了。  正确的懒汉式的单例我就不贴代码了。贴子上有很多这样的例子。

这里为了说明,用这种单例,可能会产生两个不同的method m对象,我贴下代码,大家可以验证下:

  1. public class thread1 implements Runnable{
  2. method m ;
  3. thread2 t = new thread2 ();
  4. public void run() {
  5. m = method.getInstance();
  6. m.print1();

  7. t.compareMeth(m);
  8. }

  9. }


  10. public class thread2 implements Runnable{
  11. method m ;
  12. public void run() {
  13. m = method.getInstance();
  14. m.print2();
  15. }
  16. public void compareMeth(method m){
  17. if(this.m == m)
  18.   System.out.println("这两个对象是同一对象了,即是同一锁了");
  19. else
  20.   System.out.println("这两个对象不是同一对象了,即是两把不同的锁了");
  21. }
  22. }
复制代码


从图中,可以证明。这种单例产生了两个不同的method 对象了,那么自然去掉static 修饰的b 或用 this 来做锁同步时,是两个不同的对象锁,
这两个线程当然又 去竞争CPU资源,最终会 交替的打印A、B了。

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
陈淑飞 发表于 2012-7-18 09:01
不太认可这句。 其实,b中加不加static很关键。
当加static后 b就成了静态成员变量,所有实例共享,只 ...

此处你的单例模式是懒汉模式加载,会出现一个线程进入getInstance()的if(m==null)里面,然后放弃CPU执行权,被另一个线程抢到也进入到getInstance()的if判断语句里,当线程醒的时候,就创建了两个method的实例对象
回复 使用道具 举报
柯玲 中级黑马 2012-7-18 10:39:03
7#
建议你把单例改为饿汉式加载,即:
  1.         private static method m = new method();
  2.         private method() {}
  3.         public static method getInstance() {
  4.                 return m;
  5.         }
复制代码
就不会产生两个不同的method对象,现在证明你原本的代码也不满足同步的另一个前提:多个线程操作同一个资源。
1、将创建method实例的方法改为饿汉加载,就可以保证为同一个资源。
2、操作print1()、print2()的线程都改为两个或两个以上,不要只创建一个了
回复 使用道具 举报
张凯 中级黑马 2012-7-19 20:02:02
8#
陈淑飞 发表于 2012-7-18 09:01
不太认可这句。 其实,b中加不加static很关键。
当加static后 b就成了静态成员变量,所有实例共享,只 ...

谢谢,明白了。
回复 使用道具 举报
陈欢 中级黑马 2012-7-19 22:13:40
9#
在Java中,使用synchronized关键字来进行线程的同步   
  
一个设置了synchronized关键字的方法自动成为同步方法   
进入方法则相当于以当前对象为信号量,上锁。退出时解锁。   
  
Java还提供了wait()和notify(),notifyAll()方法辅助进行同步   
wait()会解锁当前的信号量,使线程进入堵塞状态。直到使用   
同一信号量的notify()或notifyAll()来唤醒它。   

线程在不同时刻有不同的状态,正在被执行时,即占有CPU时   
我们称之为Running状态。那么其它的状态线程自然是不能运行啦。   
很可能有多个线程都不能运行吧,但它们不能运行的原因却是各不相同的。   
那么由于相同原因不能运行的线程就会存在一个“池pool”中。   
由于虚拟机线程调度而不运行的,处于   Runnable池中,表示万事俱备,   
只差CPU,由于使用synchronized而阻塞的线程就会在对象锁   
(object 's   lock   pool)池中等待。而同步的方法中调用wait()后,线程则会   
在wait池中等待。这里要注意了,首先wait()方法能得到执行说明当前线程   
正在运行(在Running状态),从而说明它肯定拥有对象锁;第二,调用   
wait()方法进入阻塞状态时,当前线程将释放对象锁(!!)第三,   
在notify()或notifyAll()方法唤醒此线程时,它将进入   object 's   lock   pool   
  池中等待,以重新获得对象锁。状态图如下所示。   
                                          Schedule   
    (Runnable)           <------------->     (Running)   
                ^                                                     |         \   
                |                                                     |           --\   
                |                                 synchronized           wait()   --must   hav   lock   
        acquire   lock                                       |                   \   release   lock   
                |                                                     |                     \   
                |                                                     |                       \           
                |                     (Blocked   in)         |                 (Bolcked   in   )   
                  ----------(   object 's   ) <----                 (   object 's     )   
                                      (   lock   pool)   <----------   (   wait   pool   )   
                                                                    notify()   
我认为是这样   这样理解可以更好回答你这个问题    按照这个思路想下去就可以理解了

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马