简体   繁体   English

Java线程同步-以正确的顺序打印数字

[英]Java Thread synchronization - printing out numbers in right order

I'm learning how to work with threads in Java and I need some advice.. 我正在学习如何在Java中使用线程,我需要一些建议。

I want to print on the standard output numbers from 0..50 with the name of the thread that has done it using three threads. 我想在0..50的标准输出数字上打印使用三个线程完成的线程的名称。

I have two classes - class Counter that implements Runnable and class Main that creates and runs the threads. 我有两个类-实现Runnable的Counter 和创建并运行线程的Main类 Counter has the variable c which is shared among the threads. 计数器具有在线程之间共享的变量c

My idea was, that I increment c by 1 and then call yield() on the current thread so as the other threads would do the same. 我的想法是,我将c加1,然后在当前线程上调用yield() ,以便其他线程也可以这样做。 Repeat this until c reaches 50. 重复此操作,直到c达到50。

But it doesen't work, the numbers are printed out in wrong order. 但这不起作用,数字打印顺序错误。 How do I fix this? 我该如何解决?

public class Counter implements Runnable {

Thread t1;
private int c = -1;

public Counter() {
}

public Counter(String name) {
    t1 = new Thread(this, name);
    t1.start();
}

@Override
public void run() {
    while (c < 50) {
        increment();
        Thread.yield();
    }
}

public void increment() {
    if (c < 50) {
        c++;
        System.out.println(Thread.currentThread().getName() + ": " + c);
    }
}
}

public class Main {

public static void main(String[] args) throws IllegalThreadStateException {
    Counter c1 = new Counter();
    Thread t1 = new Thread(c1, "Thread 1");
    Thread t2 = new Thread(c1, "Thread 2");
    Thread t3 = new Thread(c1, "Thread 3");

    t1.start();
    t2.start();
    t3.start();

}

Edit: In the end I solved it this way. 编辑:最终我以这种方式解决了它。 Thank you all who helped me with the tough start with multithreading. 谢谢所有帮助我艰难地开始多线程工作的人。

import java.util.concurrent.atomic.AtomicInteger;

public class Counter2 implements Runnable {

// you could also use simple int
private AtomicInteger c = new AtomicInteger(-1);
private static final Object syncObject = new Object();

public Counter2() {
}

@Override
public void run() {
    while (c.get() < 50) {
        synchronized (syncObject) {
            if (c.get() < 50) {
                System.out.println(Thread.currentThread().getName() + ": " + c.incrementAndGet());
            }
        }
    }
}    
}

Use syncrhonized section in method increment with special static object. 在带有特殊静态对象的方法增量中使用同步部分。

private static final Object syncObj = new Object();

public void increment()
{
  syncrhonized( syncObj )
  {
   c++;
   System.out.println(c);
  }
}

Or make this method synchronized via its declaration. 或通过其声明使此方法同步。

But it's wrong idea to store your real data in thread objects. 但是将实际数据存储在线程对象中是错误的想法。 Thread should just to manipulate with share objects but not to store them.\\ And actually I don't understand why do you start thread in 线程应该只使用共享对象进行操作,而不要存储它们。\\实际上我不明白为什么要在其中启动线程

Make increment() synchronized in order to prevent other threads from entering the method concurrently. 使increment()同步,以防止其他线程同时进入方法。

In conjunction with yield() you should be able to get another thread print the next number (not always since the system might resume the thread that called yield again - see Ingo's answer - , but the order should still be the same). yield()结合使用,您应该能够使另一个线程打印下一个数字(并非总是如此,因为系统可能会再次恢复调用yield的线程-请参阅Ingo的答案-,但顺序应相同)。

synchronized increment() would mean that any thread that tries to enter that method on the same object would have to wait if another thread would have aquired the lock already by entering the method. synchronized increment()意味着,如果另一个线程通过输入方法已经获取了锁,则试图在同一对象上输入该方法的任何线程都必须等待。

Quoting from the javadoc Thread.yield(), emphasis by me: 引用javadoc Thread.yield(),我强调:

public static void yield()

A hint to the scheduler that the current thread is willing to yield its current use of a processor. 给调度程序的提示是当前线程愿意放弃当前使用的处理器。 The scheduler is free to ignore this hint . 调度程序可以随意忽略此提示

... ...

It is rarely appropriate to use this method. 很少适合使用此方法。

Yes your code won't work. 是的,您的代码无效。 Thread#yield() won't control the thread scheduler in the manner you desire. Thread#yield()不会以您期望的方式控制线程调度程序。 I"m curious what result you get. You'll probably get repeated numbers and some number that are slightly out of order. 我很好奇您会得到什么结果。您可能会得到重复的数字,有些数字会有些混乱。

You could use atomic integer which should remove all duplicates. 您可以使用原子整数,该整数应删除所有重复项。 But since the print statement is not atomic. 但是由于print语句不是原子的。 You may still print your results out of order. 您仍然可以不按顺序打印结果。 So you should probably just synchronize the increment method. 因此,您可能应该仅同步增量方法。 Also you don't really need yield so dump it. 另外,您实际上并不需要产量,所以将其丢弃。

If the purpose of the problem is to go from thread 1 to thread 2 to thread 3 back to thread 1, etc... Such that the results are 如果问题的目的是从线程1到线程2再到线程3再回到线程1,依此类推,那么结果是

Thread 1:0
Thread 2:1
Thread 3:2
Thread 1:3
Thread 2:4
Thread 3:5
Thread 1:6
Thread 2:7
....

Then you'll need to lock the increment method and use wait and notifyAll. 然后,您需要锁定增量方法,并使用wait和notifyAll。 wait will cause other threads to halt processing until the current thread notifies them to start again. 等待将导致其他线程停止处理,直到当前线程通知它们再次启动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM