线程同步的基本问题

zq1447 2012-04-20 01:18:16
加精
转载自:http://dev.10086.cn/cmdn/bbs/thread-73315-1-1.html

有两个线程, 一个负责发送数据, 另外一个线程往第一个线程中写入数据, 然后通知它发送数据。
而第一个线程,就是等着通知, 在被通知唤醒之后就检查有没有数据需要发送, 有则发送, 然后继续睡眠。
这样一个结构很是简单, 但是我总是搞不定, 忘高手写个模型,谢谢。
class worker extend Thread
{
public synchronized void addData(..){...} // 往里面放数据
public void run()
{
while(true)
{
// 睡眠? 我不知道应该用sleep, 还是wait

checkData and SendData Here
}
}
}

另外一个线程:
{
worker aw;
....
// 如果有数据了

aw.addData(...) // z增加数据
aw.notify();// 这里通知此线程发送数据, 但是调用notify老出错, 请大侠赐教!
}


如下改造

class worker extend Thread//线程A
{
public synchronized void addData(..){...} // 往里面放数据
public void run()
{
while(true)
{
// 睡眠? 我不知道应该用sleep, 还是wait
// 使用sleep

checkData and SendData Here

唤醒线程B

}
}
}

线程B:
{
worker aw;
....

aw.addData(...) // z增加数据
//这里通知此线程发送数据, 但是调用notify老出错, 请大侠赐教!
//这里通知此线程发送数据,发送完毕就wait(),
}


谢谢大侠, 但是这个里面有疑问, 线程A里面有数据发送之后, 只管自己睡眠, 不要唤醒B,我的想法是B一直是醒着的。
在线程B中, 给A加数据之后通知A的代码是什么?通知完A之后能够不Wait么? 线程B能不能不睡眠?

Condition Objects
Often, a thread enters a critical section, only to discover that it can't proceed until a condition is fulfilled. You use a condition object to manage threads that have acquired a lock but cannot do useful work. In this section, we introduce the implementation of condition objects in the Java library. (For historical reasons, condition objects are often called condition variables.)

Let us refine our simulation of the bank. We do not want to transfer money out of an account that does not have the funds to cover the transfer. Note that we cannot use code like

if (bank.getBalance(from) >= amount)
bank.transfer(from, to, amount);



It is entirely possible that the current thread will be deactivated between the successful outcome of the test and the call to transfer.

if (bank.getBalance(from) >= amount)
// thread might be deactivated at this point
bank.transfer(from, to, amount);



By the time the thread is running again, the account balance may have fallen below the withdrawal amount. You must make sure that the thread cannot be interrupted between the test and the insertion. You do so by protecting both the test and the transfer action with a lock:

public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
{
// wait
. . .
}
// transfer funds
. . .
}
finally
{
bankLock.unlock();
}
}



Now, what do we do when there is not enough money in the account? We wait until some other thread has added funds. But this thread has just gained exclusive access to the bankLock, so no other thread has a chance to make a deposit. This is where condition objects come in.

A lock object can have one or more associated condition objects. You obtain a condition object with the newCondition method. It is customary to give each condition object a name that evokes the condition that it represents. For example, here we set up a condition object to represent the "sufficient funds" condition.

class Bank
{
public Bank()
{
. . .
sufficientFunds = bankLock.newCondition();
}
. . .
private Condition sufficientFunds;
}



If the TRansfer method finds that sufficient funds are not available, it calls

sufficientFunds.await();



The current thread is now blocked and gives up the lock. This lets in another thread that can, we hope, increase the account balance.

There is an essential difference between a thread that is waiting to acquire a lock and a thread that has called await. Once a thread calls the await method, it enters a wait set for that condition. The thread is not unblocked when the lock is available. Instead, it stays blocked until another thread has called the signalAll method on the same condition.

When another thread transfers money, then it should call

sufficientFunds.signalAll();



This call unblocks all threads that are waiting for the condition. When the threads are removed from the wait set, they are again runnable and the scheduler will eventually activate them again. At that time, they will attempt to reenter the object. As soon as the lock is available, one of them will acquire the lock and continue where it left off, returning from the call to await.

At this time, the thread should test the condition again. There is no guarantee that the condition is now fulfilledthe signalAll method merely signals to the waiting threads that it may be fulfilled at this time and that it is worth checking for the condition again.



直接使用java内部的类,如:java.util.concurrent.LinkedBlockingQueue

在线程A,线程B中共同持有(或者访问同一个)LinkedBlockingQueue

线程A调用 queue.put(msg);
线程B调用 queue.take();


对于线程B而言,如果在调用take的时候queue中没有数据,它会一直等等线程A调用put后唤醒。

线程A也会在queue到达指定大小时put处于挂起,直到线程B take出了一个数据为止。

在线程B中, 给A加数据之后通知A的代码是什么?
-> 可以,在B中自己实现一个侦听接口,给A加数据之后调用接口

通知完A之后能够不Wait么?

-> 可以不wait,但是要在线程B开始做判断且资源消耗大很多

另:不要在A中提供public synchronized void addData(..){...} 这样的方法,应该让为他们提供一个synchronized的公用数据缓冲区,如果该区为空,则把数据发送线程等待,否则唤醒并发送数据,发送之后等待

大概解决了这个问题了, 方法大致如下:
class worker extend Thread
{
public synchronized void addData(..){...} // 往里面放数据
public void run()
{
while(true)
{
// checkData and SendData Here
sleep(1000000); //没数据就睡觉
}
}
}

另外一个线程:
{
worker aw;
....
// 如果有数据了

aw.addData(...) // 增加数据
aw.sleep(0);// 这样就可以让它醒过来了
}


简单吧, 呵呵

hehe,来晚了,不过我建议改成这样:

class worker extend Thread
{
public synchronized void addData(..){
... // 往里面放数据
// 你原来的 notify() 出错就是因为还没有获得对象的 monitor
// 现在处在 synchronized 方法里,拥有了 monitor,就可以了
this.notifyAll();
}
public void run() {
while(true) {
// 要先通过 synchronized 获得 monitor,然后再 wait()
synchronized(this) {
this.wait();
}

checkData and SendData Here
}
}
}

另外一个线程:
{
worker aw;
....
// 如果有数据了

aw.addData(...) // z增加数据
}
...全文
1371 49 打赏 收藏 转发到动态 举报
写回复
用AI写文章
49 条回复
切换为时间正序
请发表友善的回复…
发表回复
枫静 2012-04-26
  • 打赏
  • 举报
回复
支持你的想法,观望
  • 打赏
  • 举报
回复
看不懂正在学习中
lestchun 2012-04-26
  • 打赏
  • 举报
回复
恩 挺好的
程序员小白 2012-04-26
  • 打赏
  • 举报
回复
鼓掌 不错
dfasri 2012-04-25
  • 打赏
  • 举报
回复
这种方式, 是利用了JAVA特有的功能做到的, 没有在逻辑上去分析出结果, 是利用了便利使得结果相同而已.

就像要 停止线程, 有人会直接用Kill来退出线程了事, 有人会写退出消息让线程自然退出, 也有人会采用库特有的功能 Exit 之类的 来让线程"安全"退出.

但这些不值得推荐的, 原因是在底层到底做了多少东西才实现这些功能的, 你不会知道, 但往往这些就跟效果相挂钩.

还是不要单纯去追求实现, 而应该要学习正确的逻辑处理. 这样的一写一读的模型, 很容易做. 我之前发过的代码也有. 做程序员就应该有点研究精神, 不是找个方法实现就算了.
Payden 2012-04-25
  • 打赏
  • 举报
回复
很不错的文章啊收藏
cyllfsl 2012-04-25
  • 打赏
  • 举报
回复
不错 ,很好
dearhjb 2012-04-24
  • 打赏
  • 举报
回复
这个主要看以后的方法吧,只是编码用处不大,但是要往高处走就必须要了!
  • 打赏
  • 举报
回复
java 啊
xmwzidcrose 2012-04-24
  • 打赏
  • 举报
回复
看看,一头雾水
sunquanguang01 2012-04-24
  • 打赏
  • 举报
回复
要是能讲讲线程调试的技巧那就更好了。
杨康 2012-04-24
  • 打赏
  • 举报
回复
这个必须支持!
Think.Chen 2012-04-23
  • 打赏
  • 举报
回复
不太懂
totorest 2012-04-23
  • 打赏
  • 举报
回复
long long long
liheng0509 2012-04-22
  • 打赏
  • 举报
回复
太强大了
whdrs 2012-04-22
  • 打赏
  • 举报
回复
毛线的,看不明白。
beautymeinv 2012-04-22
  • 打赏
  • 举报
回复
不太懂
HUMPHREYLJH 2012-04-22
  • 打赏
  • 举报
回复
……晕啊
zmkkobe 2012-04-21
  • 打赏
  • 举报
回复
很好,不错,仔细看看
「已注销」 2012-04-21
  • 打赏
  • 举报
回复
我打死你们!
加载更多回复(16)

790

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 其他移动相关
社区管理员
  • 移动开发其他问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧