【发布时间】:2020-10-01 21:36:52
【问题描述】:
晚上好!亲爱的,我提前为糟糕的文字道歉。英语不是我的母语。问题是这样的:有代码,我什至想出了死锁是如何实现的。但是如何摆脱它是个问题。 有人告诉我要么同步捕获,要么按顺序调用线程。
我尝试如下:
- 我从方法中删除了监视器,并在类中创建了一个静态对象并将其扔到监视器中,如果你调用线程,它会工作,但如果你将线程调用到循环中,一切都会崩溃。
- 我还尝试将函数 bower.bowBack (this) 重写为 this.bowBack (bower),以便不会从 1 个线程调用 2 个对象,但仍然值得将其放入循环中,我们再次不这样做'没有我们需要的东西。
请告诉我如何解决这个问题?
更新:我很抱歉没有提供信息。我会改进的。 根据作业,需要输出2个流:
- A:G 打了我
- 答:我还击
- G:他开枪打死我
(流 1)
- G:他开枪打死我
- G:我还击
- A:G 打了我。
(流 2)
他就是这么做的。但是,一旦您将其放入循环中,它就会冻结。如何组织线程的顺序工作?我不明白如何正确实现它。
package pr1;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//package ru.philosophyit;
public class DeadLock {
static class Friend {
private final String name;
private final Lock lock = new ReentrantLock();
public Friend(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void bow(Friend bower) {
boolean Lock_my = false;
boolean Lock_bower = false;
try
{
while(!Lock_my || !Lock_bower)
{
Lock_my = this.lock.tryLock(100, TimeUnit.MILLISECONDS);
Lock_bower = bower.lock.tryLock(100, TimeUnit.MILLISECONDS);
}
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
System.out.format("%s: стреляю в ответ!\n", this.name);
bower.bowBack(this);
this.lock.unlock();
bower.lock.unlock();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
public void bowBack(Friend bower) {
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
}
}
/**
* Точка входа в программу
*
* @param args аргументы командной строки
*/
public static void main(String[] args) {
Friend alphonse = new Friend("Alphonse");
Friend gaston = new Friend("Gaston");
for (int i = 0; i < 5; i++)
{
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
}
}
}
我还对静态对象进行了同步,并且循环正常工作,但输出不是连续的。即可以有 1 个流 1 个流 2 个流 2 个流 1 个流,而不是 1 个流然后 2 个流。我不知道为什么。
package pr1;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//package ru.philosophyit;
public class DeadLock {
static Object o = new Object();
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void bow(Friend bower) {
//synchronized(getClass())
synchronized(o)
{
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
System.out.format("%s: стреляю в ответ!\n", this.name);
bower.bowBack(this);
}
}
public void bowBack(Friend bower)
{
//synchronized(getClass())
synchronized(o)
{
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
}
}
}
public static void main(String[] args) {
Friend alphonse = new Friend("Alphonse");
Friend gaston = new Friend("Gaston");
// for (int i = 0; i < 10; i++)
{
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
}
}
}
事实证明2,代码非常合适,线程工作的顺序无关紧要,但消息的顺序很重要。但是方法 2 变得复杂,因为当线程增加时,我们需要大量的同步对象。他们说你只需要改变几行,一切都会奏效。 3代码是原始形式的任务本身的代码(因此您需要更改行)。 我知道 2 个线程使用不同的对象,因此同步不能正常工作(我们在 bowBack 上阻塞,也许值得重写它?)
package pr1;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//package ru.philosophyit;
public class DeadLock {
static Object o = new Object();
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return name;
}
public synchronized void bow(Friend bower) {
{
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
System.out.format("%s: стреляю в ответ!\n", this.name);
bower.bowBack(this);
}
}
public synchronized void bowBack(Friend bower)
{
{
System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
}
}
}
public static void main(String[] args) {
Friend alphonse = new Friend("Alphonse");
Friend gaston = new Friend("Gaston");
for (int i = 0; i < 10; i++)
{
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
}
}
}
【问题讨论】:
-
语言障碍是一回事,但如果您可以发布 MCVE 并指出您遇到的确切错误/情况,那就太好了。 “一切都崩溃了”和“我们没有我们需要的东西”并不是那么有帮助。
-
为了避免死锁,您需要在所有线程中以相同的顺序锁定对象,例如首先锁定具有较小哈希码的对象。或者您可以使用
Lock接口和方法tryLock的实现与超时 -
欢迎来到 StackOverflow! “如果你调用线程,它会起作用,但如果你把线程调用成一个循环,一切都会崩溃”。您能否编辑您的问题以包含将线程调用到循环中的代码?这将帮助我们区分有效/无效的情况。
-
@Ivan 我尝试使用超时的 tryLock,但问题仍然存在于循环中。如果我运行它,一切都会进入睡眠状态。显然我不太明白如何正确实现这一点
-
你为什么要启动 10 个线程?关于谁在空闲时再次抓住锁,您实际上没有任何流量控制。还要注意你的两个锁循环,很可能一个线程会抓住一个锁,然后另一个线程会抓住另一个锁,然后他们永远不会脱离那个循环。这与锁定顺序有关。
标签: java multithreading oop object deadlock