[英]i want to test java multithread ,but only one thread run
public class ThreadText
{
public static void main(String[] args)
{
windows w=new windows();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
Thread t4=new Thread(w);/*four threads*/
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class windows implements Runnable
{
int tickets=1000;
public void run()
{
synchronized(this)
{
while(tickets>0)
{
System.out.println(Thread.currentThread().getName()+" is saling"+tickets);
tickets--;
}
}/*i think problem is here*/
}
}
当我不使用同步时,所有线程都运行,但结果是错误的。 有些票号相同。正确的结果是每个 window 得到不同的票号。
当您同步整个循环时,第一个线程将获得锁并倒计时到 0。其他 3 个线程将等到第一个线程释放锁,此时它们无事可做。 实际上,结果是单线程的。
要正常工作,您需要在循环内部进行同步,以便其他线程也可以工作。
但是,第一个线程可能仍然太快,以至于它最终完成了所有工作。 为了更好地模拟你想要的东西,在同步块之外添加一个延迟(你知道,进行销售需要时间) 。 在下面的代码中添加了 1ms 的最小延迟。
最后,Java 命名约定是类名以大写字母开头,因此应命名为Windows
。
class Windows implements Runnable {
private int tickets = 1000;
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (;;) { // loop forever
synchronized (this){
if (this.tickets == 0)
break;
System.out.println(threadName + " is saling " + this.tickets);
this.tickets--;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
break;
}
}
}
}
输出
Thread-0 is saling 1000
Thread-2 is saling 999
Thread-3 is saling 998
Thread-1 is saling 997
Thread-0 is saling 996
Thread-1 is saling 995
Thread-2 is saling 994
Thread-3 is saling 993
Thread-0 is saling 992
Thread-1 is saling 991
Thread-3 is saling 990
Thread-2 is saling 989
. . .
Thread-2 is saling 11
Thread-1 is saling 10
Thread-0 is saling 9
Thread-3 is saling 8
Thread-0 is saling 7
Thread-1 is saling 6
Thread-2 is saling 5
Thread-1 is saling 4
Thread-0 is saling 3
Thread-3 is saling 2
Thread-2 is saling 1
您所遇到的情况称为竞争条件。 这意味着由于线程操作的顺序,您可以让两个线程认为它们减少了某些东西,但只有一个减少了,等等。
为了解决这个问题,您有两个选择:
synchronized
——这是相当繁重的,除非你把它放在最小的关键部分,否则会太多。AtomicInteger
确保整数在线程间安全地递增或递减。 要修复您的示例并仍然使用synchronized
,您的代码应如下所示:
class windows implements Runnable
{
int tickets=1000;
public void run()
{
while(tickets>0)
{
synchronized(this)
{
System.out.println(Thread.currentThread().getName()+" is saling"+tickets);
tickets--;
}
}
}
}
当然,不利的一面是您必须记住synchronize
对变量的所有访问。
使用AtomicInteger
过程更容易控制:
class windows implements Runnable
{
AtomicInteger tickets = new AtomicInteger(1000);
public void run()
{
while(tickets.get() > 0)
{
System.out.println(Thread.currentThread().getName()
+ " is saling" + tickets.decrementAndGet());
}
}
}
由于多个线程访问票证成员,因此您确实需要同步。 但是因为您将 run 方法基于 while 票证 > 0,所以只有 1 个线程会一直运行到最后。 一种解决方案是将线程的执行与另一个变量联系起来,让 run 方法只减少一次票证,然后休眠,以便其他线程有机会运行。 例如,如果您按如下方式重构您的 windows 类,那么它会做我认为您希望它做的事情。
class windows implements Runnable {
volatile boolean finished = false;
int tickets = 1000;
public void run() {
while(!finished) {
synchronized(this) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName()+" is selling " + tickets);
tickets--;
if (tickets <= 0) {
finished = true;
}
}
}
try {
Thread.sleep(10);
}
catch(InterruptedException ex) {
}
}
}
}
从您的代码中,我了解到您正试图在现实生活中为门票复制 Sales Window。 类比(查看答案底部的图片链接):售票场所(例如,音乐会门票)。 这个地方有多个部分/窗口,从那里进行货币兑换并提供音乐会门票。 Window后面有一个人实际拿钱给你票。 因为在你的例子中没有任何与金钱相关的东西,所以这只是一种单方面的交换。 基本上,window 背后的人会在客户来时给票(客户来就像您的示例中执行的线程)。
只有有限数量的 Ticket,每张 Ticket 都有一个唯一的票号,介于 1 到 1000 之间。
您的代码具有相同的 Runnable 实例 Window。并且为所有线程(线程的所有不同实例)执行提供了相同的实例。 所以你的现实生活设计变成了这样:同一个人会给你 Window 1、2、3 等的票。但是这个人一次只能照顾一张 window。 因此,使您的代码有效地作为单处理器/核心代码运行,但性能比顺序代码差,因为线程切换(如果发生)将产生更多开销。
现在假设我们有多个销售人员 - 每个 window 一个。那么问题就变成了不由不同的销售人员出售同一张票(竞争条件)。
为此,所有销售人员都必须一次从盒子里取出一张票。 IE 获取票证的部分是唯一需要同步的部分。 这最好通过原子整数来实现。 其他答案在其同步块中包含的代码块超出了要求。 @Berin Loritsch 回复已经回答了这部分(按照我的说法)。
class TicketSeller /* Not windows*/ implements Runnable {
AtomicInteger tickets = new AtomicInteger(1000);
public void run() {
while (tickets.get() > 0) {
System.out.println(Thread.currentThread().getName()
+ " is saling" + tickets.decrementAndGet());
}
}
}
public class ThreadText {
public static void main(String[] args) {
Thread t1 = new Thread(new TicketSeller());
Thread t2 = new Thread(new TicketSeller());
Thread t3 = new Thread(new TicketSeller());
Thread t4 = new Thread(new TicketSeller());
/*four threads, four windows from where the ticket can be provided by 4
different TicketSellers*/
t1.start();
t2.start();
t3.start();
t4.start();
}
}
有了这个,当一个不同的线程开始执行或多个线程并行运行时,线程不会进入不必要的睡眠状态(顺便说一下,睡眠甚至不能确认避免了竞争条件)并且只会在有限的时间内被阻止执行代码区域,即 tickets.get()。
当然,您可以提前几步 go 进一步优化它。 但这需要更深入的理解和复杂的思考。 例如,销售人员被分配了一定数量的门票。 门票洗牌发生,以便通过管理每个销售人员拥有的门票的东西来优化在最快的执行时间内出售最大数量的门票。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.