多线程中的锁是否应始终保持不变?

时间:2019-01-13 16:40:08

标签: java multithreading

我正在尝试下面的代码片段,它给出了预期的输出,但是我很好奇我要创建1或2个锁定对象吗?

package com.practice;

class A{
    String lock="";

    A(String lock){
        this.lock = lock;
    }

    A(){ }

    public  void printMe(int x) {
        int counter = x;
        synchronized (lock) {
            System.out.println(++counter);
        }
    }
}



public class MultiThreadingPractice {

    public static void main(String[] args) {
        A a = new A("1");

        Thread t1=new Thread(() -> {
                a.printMe(1);
        });

        a.lock = new String();

        Thread t2=new Thread(() -> {
                a.printMe(1);
        });

        t1.start();
        t2.start();
    }
}

3 个答案:

答案 0 :(得分:2)

多线程中的锁应始终保持不变吗?是的。

您正在使用字符串(任何对象都可以)作为锁。当您为锁分配一个新值(新字符串)时,这意味着您周围有多个锁实例。只要所有线程都在同一个锁实例上同步就可以,但是您的class A代码中没有任何公开的内容可以确保这种情况。

在示例中的实际使用中,您是安全的。由于只有在将锁设置为第三个实例和最后一个实例后,才会启动线程,因此直到稳定之前,都不会尝试对锁进行同步。 (3个实例:第一个是对空字符串的初始化;第二个是对提供的构造函数参数“ 1”的初始化;第三个是对另一个空字符串的显式赋值)。因此,尽管此代码“有效”,但只能通过我所说的“巧合”来起作用,即,它在设计上不是线程安全的。

但是,让我们假设您在构造每个线程后立即启动每个线程的情况。这意味着您将在t1运行之后但甚至在t2创建之前重新分配锁定成员。

过一会儿,两个线程将在新的锁实例上同步,但是在您切换锁的那一刻左右,线程t1可能并且可能在使用旧锁的synchronized(lock) { ... }子句中实例。大约在那个时候,线程t2可以执行并尝试在新的锁实例上进行同步。

简而言之,您已经在打算用来消除计时窗口的机制中创建了计时窗口(种族危险)。

您可以安排进一步的同步级别,以使您可以替换锁,但是我无法想象任何直接的情况都是必要的,有用的或明智的。最好在发生任何争用之前分配一个锁,然后坚持使用该锁。

P.S。 “我要制造多少把锁?” 3.尽管前两个从未使用过。

答案 1 :(得分:0)

  

想知道我要创建1个或2个锁定对象吗?

程序中的这一行在加载程序时会创建一个String实例,并且您的两个A实例均以对同一String的引用开头。

String lock="";

此行创建第​​二个String实例,因为new String(...) 总是会创建一个新对象,而不管是否有其他具有相同值的String都已经 interned

a.lock = new String();

答案 2 :(得分:0)

多线程中的锁应始终保持不变吗?否

典型的同步方法是使用同步方法,而不是单独的锁。这种方法不太容易出错,因此是首选。等效于将当前对象用作锁:

synchronized(this) {
          ...
}

并且由于this对象通常不是不可变的,因此我们可以得出结论,将可变对象用作锁是一种常见的做法。

当您更改对锁定对象的引用(是的,您要更改引用而不是锁定对象本身)时,您在代码中所做的工作是非常糟糕的方法。它会引发编程错误。