synchronized依赖于字符串初始化

时间:2015-02-18 18:42:01

标签: java string synchronization

我正在尝试使用String对象作为锁(是的,我知道不推荐它 - 它仅用于锁定理解)。

在创建锁时初始化锁定时,一切正常:

代码:

public class Action{

    String lock  = new String("hello");

    @Override
    public String myAction(long threadId){

        synchronized (lock) {
            i++;
            printToLog("thread " + threadId);

            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                printToLog("Thread "+threadId+ " could not sleep "+ e.getMessage() + "\n");
            }
            printToLog("Thread "+threadId+ " slept well! ");
        }
        return i+"";
    }

    private void printToLog(String messege) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date(System.currentTimeMillis());
        logger.info(messege + " at time: "+dateFormat.format(date));
    }

}

日志:

  

[主题555时间:2015/02/18 18:16:03]
  [线程555睡得很好!时间:2015/02/18 18:17:43]
  [主题557时间:2015/02/18 18:17:43]
  [线程557睡得很好!时间:2015/02/18 18:19:23]
  [线程556时间:2015/02/18 18:19:23]
  [线程556睡得很好!时间:2015/02/18 18:21:03]

但是当我在方法中初始化实例成员锁时,就好像同步不起作用。

代码:

public class Action{

    String lock;

    @Override
    public String myAction(long threadId){
        lock = new String("hello");
        synchronized (lock) {
            i++;
            printToLog("thread " + threadId);

            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                printToLog("Thread "+threadId+ " could not sleep "+ e.getMessage() + "\n");
            }
            printToLog("Thread "+threadId+ " slept well! ");
        }
        return i+"";
    }

    private void printToLog(String messege) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date(System.currentTimeMillis());
        logger.info(messege + " at time: "+dateFormat.format(date));
    }

}

日志:

  

[主题555时间:2015/02/18 19:17:35]
  [主题556时间:2015/02/18 19:17:40]
  [主题556时间:2015/02/18 19:17:41]
  [主题557时间:2015/02/18 19:17:44]
  [主题560时间:2015/02/18 19:17:48]
  [线程555睡得很好!时间:2015/02/18 19:19:15]
  [线程556睡得很好!时间:2015/02/18 19:19:20]
  [线程556睡得很好!时间:2015/02/18 19:19:21]
  [线程557睡得很好!时间:2015/02/18 19:19:24]
  [线程560睡得很好!时间:2015/02/18 19:19:28]

我不明白启动对象的是什么?更多 - 它是一个字符串对象所以无论如何它不应该是相同的参考?我在想什么?

编辑:是的,我不小心交换了代码..当你的“帖子似乎包含代码格式不正确的代码”太多次然后找出它时会发生什么是由于日志行:|将修复掉期。

1 个答案:

答案 0 :(得分:3)

(您可能已经交换了代码片段)。

我认为你错过了Java synchronization的一个重要方面。您不能使用字段 lock 作为锁定为某个实例分配锁。或者如手册中所述:

  

与synchronized方法不同,synchronized语句必须指定提供内部锁的对象

那么你的第二个问题(问题代码片段中的第一个)是你创建一个新对象,即:

lock = new String("hello");

然后你锁定在该对象上。这意味着每个线程都有一个锁定的不同实例。因此根本没有同步。

示例:假设您有两个帖子t1t2。首先,我们运行线程1的一部分。线程1调用该方法并创建值为String的{​​{1}}实例。所以内存看起来像:

"hello"

接下来你锁定了那个对象,所以标记为+------+ +---------------+ |Action| <------------------+ thread memory | +------+ +-------+ +---------------+ | lock------->|String | +------+ +-------+ |"hello"| +-------+ 并带有lock,你得到:

&

现在线程+------+ +---------------+ |Action| <------------------+ thread memory | +------+ +-------+ +---------------+ | lock------->|String | & +------+ +-------+ |"hello"| +-------+ 进来了。t2做的第一件事就是创建一个新对象:

t2

因此新对象未锁定,线程+------+ +----------------+ |Action| <------------------+ thread2 memory | (old object) +------+ +-------+ +----------------+ +-------+ | lock------->|String | |String | & +------+ +-------+ +-------+ |"hello"| |"hello"| +-------+ +-------+ 可以继续执行。


但是,您的代码可能会部分锁定。例如,线程t2可能首先创建一个对象,然后设置t1的字段,然后暂停。然后Action变为活动状态,同时创建一个对象,设置t2字段,因此两个线程最终都使用相同的对象,但这不太可能。


最后需要注意的是,使用&#34; Action's作为锁&#34; 有没问题。首先,你不能使用String作为锁定,你应该将其视为&#34;锁定&#34;字符串。因此,虚拟机只是将信息附加到锁定的对象。因此,没有好的对象被锁定。