【问题标题】:Thread.sleep is blocking other thread also, working on other method, along with itself callled inside synchronized methodThread.sleep 也在阻塞其他线程,处理其他方法,以及在同步方法中调用的自身
【发布时间】:2017-03-12 10:04:42
【问题描述】:
class Common
{

public synchronized void synchronizedMethod1() 
    {
        System.out.println("synchronized Method1 called");
        try
        {
        Thread.sleep(1000);
        } 
        catch (InterruptedException e) 
        {
        e.printStackTrace();
        }
        System.out.println("synchronized Method1 done");
    }

public synchronized void synchronizedMethod2() 
{
    System.out.println("synchronized Method2 called");
    try
    {
    Thread.sleep(1000);
    }
    catch (InterruptedException e) 
    {
    e.printStackTrace();
    }
    System.out.println("synchronized Method2 done");
}
}

在上面的类中,我有两个同步方法,我从另一个类的 run 方法调用它们。其他类代码如下:

public class ThreadClass implements Runnable
{
    private int id = 0;
    private Common common;

public ThreadClass(int no, Common object)
{
    common = object;
    id = no;
}

public void run()
{
    System.out.println("Running Thread " + Thread.currentThread().getName());
    try 
    {
        if (id == 11) 
        {
            common.synchronizedMethod1();
        } 
        else
        {
            common.synchronizedMethod2();
        }
    } 
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

public static void main(String[] args)
{
    Common c = new Common();
    ThreadClass tc = new ThreadClass(11, c);
    ThreadClass tc1 = new ThreadClass(20, c);
    Thread t1 = new Thread(tc, "Thread 1");
    Thread t2 = new Thread(tc1, "Thread 2");
    t1.start();
    t2.start();
}
}

从 main 方法我开始两个不同的线程。在运行方法中,我给出了将两个不同的线程发送到不同的同步方法的条件。代码产生的输出是:

Running Thread Thread 2
Running Thread Thread 1
synchronized Method2 called
synchronized Method2 done
synchronized Method1 called
synchronized Method1 done

我的输出问题是: 当线程 2 进入同步方法 2 时,它会打印第三行输出并休眠 1 秒。现在,由于线程 1 没有被任何东西阻塞,所以它应该在第 3 行输出之后执行并打印第 5 行输出,然后应该进入睡眠状态,但是当线程 2 进入睡眠状态时,它不会发生这种情况,它会使线程 1 也睡眠然后第一个线程 2 完成它的执行,之后线程 1 完成它的执行。

如果我从方法中删除同步关键字,则不会发生这种行为。 能否请您解释一下使用和不使用同步关键字处理代码的不同方式背后的原因。

提前致谢。

【问题讨论】:

  • 因为线程1没有被任何东西阻塞:它阻塞了,因为它需要进入同步方法,但是不能因为线程2 已经持有锁(并且在持有时正在睡觉)。这就是同步方法的全部原理。
  • @Nizet:我知道同步关键字的概念,但由于我的程序没有遵循它,这就是为什么我对关键字的工作感到困惑。同样在我的程序中,两个线程都作用于不同的同步方法,所以如果线程 2 阻塞了方法 1 并进入睡眠状态,那么它不应该阻碍作用于其他同步方法的线程 1 的工作。这是我在帖子中询问为什么线程 2 阻止线程 1 进入其他未被自身阻止的同步方法。

标签: java multithreading synchronized


【解决方案1】:

如果我从方法中删除同步关键字,则不会发生这种行为。能否请您解释一下使用和不使用同步关键字处理代码的不同方式背后的原因。

这实际上是synchronized 关键字的全部目的。当您有多个同一个类的同步实例方法时,一次可能只执行一个。你写了这个:

class Common {
    public synchronized void synchronizedMethod1(){}
    public synchronized void synchronizedMethod2(){}
}

因为这两种方法都是同步的,所以一次只能执行一个。其中一个无法启动,另一个已完成。

这是如何工作的?简而言之,您有一个Common 对象并调用它的同步实例方法。当您调用synchronzedMethod1 时,该方法将“锁定”Common 对象(称为“获取锁定”)。虽然该方法对该Common 对象具有该锁定,但如果您尝试在该同一对象上调用任何其他同步方法,它会尝试锁定它并且它会发现它已经被锁定。因此,任何其他锁定对象的尝试都会挂起,直到他们可以这样做。一旦synchronizedMethod1 完成,它将解锁Common 对象(称为“释放锁”),然后任何人都可以尝试锁定它,例如synchronzedMethod2

因此,简而言之,synchronized 专门使您不能同时发生同一类的两个同步方法。这很有用,因为一些有问题的行为可能来自不这样做。例如,ArrayList 不这样做,因此如果一个线程尝试将对象添加到 ArrayList 而另一个尝试迭代它,它可能会抛出一个 ConcurrentModificationException 并让每个人都感到难过。

【讨论】:

  • 感谢您如此详细的回复。通过您的回答并在网上搜索有关同步方法的更详细概念后,我了解了我的代码的行为。
  • @AbuzarSiddiqui 如果此答案或任何答案解决了您的问题,请单击复选标记考虑accepting it。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己提供了一些声誉。没有义务这样做。
  • 很抱歉最近接受了您的回答。再次感谢您的精彩回复。
【解决方案2】:

休眠线程不会释放其锁,但您可以将您的sleep(...) 调用替换为wait(...)。但请记住,只有调用了 wait(...) 的对象的锁才会被释放,因此如果您希望在等待时释放多个锁,则必须设计不同的解决方案。

【讨论】:

  • 最好你还提到 sleep() 是一个静态方法,这意味着只影响调用者线程,但 wait 是一个必须通过实例访问的实例方法
【解决方案3】:

同步一个方法并不意味着同步的只是方法本身

synchronized void x(){}

等于:

 void x(){ 
  synchronised(this){}
 }

由于两个线程都访问相同的Common 实例,因此第一个线程将获得公共对象锁的所有权,这与调用哪个同步方法无关,它只会在此方法主体完成其工作后释放此锁。 如果您发送两个 Common 实例,则不会有问题,因为它们不是静态的。你也可能对ReentrantLock感兴趣

【讨论】:

    【解决方案4】:

    首先synchronized关键字是用来定义互斥的。这里通过Monitor概念实现互斥。还有一件事是sleep 不释放监视器。它只是暂停当前线程的执行一段时间。其他需要monitor的线程必须等待,直到获取monitor的线程释放它。

    synchronized有两种使用方式...

    第一个是使用同步块。

    synchronized(obj){...}
    

    这里如果任何线程想要进入同步块,它必须得到 obj 的监视器。

    第二个是使用同步方法。

    synchronized void meth(){...}
    

    同步方法和块之间的主要区别是同步方法使用它自己的对象监控器,同步块可以监控任何对象

    同步方法可以使用同步块定义如下...

    void meth(){
        synchronized (this){
            //method body
        }
    }
    

    现在您可以使用同步块来防止阻塞另一个方法的问题。这里你必须在不同的对象上定义同步块,这样两个方法可以并发执行,但多个线程不能同时执行同一个方法。

    【讨论】:

    • '使用'。 '达到'。 '推迟'。 '有两种方法'。 '它必须得到...'。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-27
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    相关资源
    最近更新 更多