Guimashisqll
2015-07-30 20:11
采纳率: 50%
浏览 1.7k
已采纳

Java 多线程问题 synchronized(obj)的疑惑

是这样的,我想知道synchronized(obj)的用法,然后写了如下的代码,运行结果我如何都搞不清,还请各位指点。

代码1:

 public class MyObj 
{
    private Integer a;
    private Double b;
    private Integer c;

    public void setA()
    {
        System.out.println(Thread.currentThread().getName() + "抢到锁..setA");
        synchronized(c)
        {
            System.out.println("    " + Thread.currentThread().getName() + " " + this.a + "  " + this.b);
            this.a = 1;
            this.b = 2.0;
            try{
                Thread.sleep(200);//(int) (Math.random()*2000)
            }catch(InterruptedException e)          {
                e.printStackTrace();
            }
            System.out.println("   " + Thread.currentThread().getName() + " " + this.a + "  " + this.b);
        }
    }
    public void setB()
    {
        System.out.println(Thread.currentThread().getName() + "抢到锁....setB");
        synchronized(c)
        {
            System.out.println("    " + Thread.currentThread().getName() + " " + this.a + "  " + this.b);
            this.a = 3;
            this.b = 4.0;
            try{
                Thread.sleep(200);//(int) (Math.random()*2000)
            }catch(InterruptedException e)          {
                e.printStackTrace();
            }
            System.out.println("    " + Thread.currentThread().getName() + " " + this.a + " " + this.b);
        }
    }
    public MyObj()
    {
        this.a = -1;
        this.b = -1.0;
        this.c = 0;
    }
}

代码2:

 public class Change 
{
    private MyObj myObj;

    public Change(MyObj myObj)
    {
        this.myObj = myObj;
    }

    public static void main(String args[])
    {
        final MyObj myObj = new MyObj();
        final Integer a = 1;

        Thread thread1 = new Thread(new Runnable(){
            public void run()
            {
                for(int product = 1; product <= 3; ++product)
                {
                    try{
                        Thread.sleep((int) (Math.random()*200));//(int) (Math.random()*2000)
                        myObj.setA();
                    }catch(InterruptedException e)
                    {
                        e.printStackTrace();
                    }

                }
            }
        },"thread1");

        Thread thread2 = new Thread(new Runnable(){
            public void run()
            {
                for(int product = 1; product <= 3; ++product)
                {
                    try{
                        Thread.sleep((int) (Math.random()*200));//(int) (Math.random()*2000)
                        myObj.setB();
                    }catch(InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        },"thread2");

        thread1.start();
        thread2.start();

    }
}

输出结果(之一):
thread2抢到锁....setB
thread2 -1 -1.0
thread1抢到锁..setA
thread2 3 4.0
thread1 3 4.0
thread2抢到锁....setB
thread1 1 2.0
thread2 1 2.0
thread1抢到锁..setA
thread2 3 4.0
thread1 3 4.0
thread2抢到锁....setB
thread1 1 2.0
thread2 1 2.0
thread1抢到锁..setA
thread2 3 4.0
thread1 3 4.0
thread1 1 2.0

**  但是**,如果将 
 System.out.println(Thread.currentThread().getName() + "抢到锁....setA");
 和
 System.out.println(Thread.currentThread().getName() + "抢到锁....setB");

放到 synchronized(c) 块里面,那么结果之一:
thread1抢到锁..setA
thread1 -1 -1.0
thread1 1 2.0
thread2抢到锁....setB
thread2 1 2.0
thread2 3 4.0
thread1抢到锁..setA
thread1 3 4.0
thread1 1 2.0
thread2抢到锁....setB
thread2 1 2.0
thread2 3 4.0
thread1抢到锁..setA
thread1 3 4.0
thread1 1 2.0
thread2抢到锁....setB
thread2 1 2.0
thread2 3 4.0

可能 输出有先后,但是 至少 thread1,thread2的输出之间,不会相互干扰。。。

我仔细观察了,上面的结果,虽然相互干扰,但是也不会出错,但是为什么 会这样呢? 既然 已经进入 thread1了,thread2怎么还能得到锁呢。。如果这样的话,synchronized(obj)还有啥意思啊(obj不一定是this)? 。实在没搞懂啊。再次麻烦各位帮忙。

总共就1个币,谅解哈。

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

6条回答 默认 最新

  • fanst_ 2015-07-31 14:55
    已采纳

    还是强调下面这种写法,在打印“抢到锁”时,实际不一定抢到锁了,因为日志是在synchronized前面打印的,只有在内部打印才是确实抢到锁~:
    public void setB()
    {
    System.out.println(Thread.currentThread().getName() + "抢到锁....setB");
    synchronized(c)
    {

    这样是不是容易理解点:
    thread2抢到锁....setB ----进入thread2,明确这里不是同步块,不受synchronized影响
    thread2 -1 -1.0 -----thread2获得锁,这是同步块里面第一句日志,确定是获取到锁了
    thread1抢到锁..setA -----进入thread1
    thread2 3 4.0 -----thread2释放锁,这是同步块最后一句日志,打完就可以认为是释放锁了
    thread1 3 4.0 -----thread1获得锁
    thread2抢到锁....setB -----进入thread2
    thread1 1 2.0 -----thread1释放锁
    thread2 1 2.0
    thread1抢到锁..setA
    thread2 3 4.0
    thread1 3 4.0
    thread2抢到锁....setB
    thread1 1 2.0
    thread2 1 2.0
    thread1抢到锁..setA
    thread2 3 4.0
    thread1 3 4.0
    thread1 1 2.0

    点赞 评论
  • bdmh 2015-07-30 21:17

    obj要是一个全局的静态的object

    点赞 评论
  • dear风会停息 2015-07-31 00:24

    由于synchronized保护的代码块为同步的代码块,一次只允许一个线程对其进行操作

    点赞 评论
  • fanst_ 2015-07-31 14:34

    我猜测你是对前者的日志打印有疑问,可能理解有误,前后两种情况你对a b赋值的操作都是同步块中,因此对于a b值的输出都是成对的,不会错乱。
    但这里下面这种情况,实际上日志打印的时候,是还没有获取到锁的,打印日志后会在synchronized这里阻塞,然后获取到锁之后才执行同步块的语句,也就是说进入thread不代表已经获取到了锁,这是两个过程。
    public void setB()
    {
    System.out.println(Thread.currentThread().getName() + "抢到锁....setB");
    synchronized(c)
    {

    点赞 评论
  • Guimashisqll 2015-07-31 14:56

    @ fanst_
    您好,这是前者的输出结果 精简版,
    thread2抢到锁....setB
    thread2 -1 -1.0
    thread1抢到锁..setA
    thread2 3 4.0
    thread1 3 4.0
    thread1 1 2.0

    这是后者的输出结果精简版,
    thread2抢到锁...setB
    thread2 -1 -1.0
    thread2 3 4.0
    thread1抢到锁..setA
    thread1 3 4.0
    thread1 1 2.0

    不知道您看到差别了没,我的代码里面,前后两者 唯一的差别就是System.out.println(Thread.currentThread().getName() + "抢到锁....setA"); 和 System.out.println(Thread.currentThread().getName() + "抢到锁....setB"); 是否放在synchronized(c) 里面。 但是 这样的输出
    thread2 -1 -1.0
    thread2 3 4.0
    是都放在里面的, 那为何会出现
    thread2 -1 -1.0
    thread1抢到锁..setA
    thread2 3 4.0
    这种情况呢,您看到了么,本来连在一起输出的,被另一个线程给 分隔开了。。。
    另外,我还想问一下,synchronized(obj)的主要作用都有啥呢。 新手,前两天刚看多线程,还请您谅解一下,谢谢!

    点赞 评论
  • fanst_ 2015-07-31 15:07

    1.线程1打印 A B C三句日志,线程2打印a b c 三句日志,在没有任何锁的时候,日志顺序是错乱的,不会是连续的ABC 或 连续的 abc,因为两个线程
    互不影响,谁抢到cpu谁就打印日志。
    2.在两个线程对同一个对象加锁时(强调是同一个对象),例如:
    线程1打印顺序:
    A
    synchronized(obj)
    {
    B
    C
    }
    线程2打印顺序:
    a
    synchronized(obj)
    {
    b
    c
    }
    ,这时候日志打印什么效果呢?肯定是先打印B再打印C 或者先打印b再打印c,但是BC或bc之间,很可能插入a或者A,因为这俩打印不受锁的影响。
    但绝对不会出现B b C c这种情况!

    点赞 评论

相关推荐 更多相似问题