多线程Java的Hello World

时间:2015-08-30 10:18:52

标签: java multithreading

我想要了解如何使用关键字:wait,notify / All,synchronized,所以我决定尝试一个简单的例子。基本上我要做的是创建两个打印字符串的线程。第一个线程的字符串为“Hello”,而第二个线程的字符串为“World”。

我想要达到的输出如下: 你好 世界 你好 世界 你好 世界 ...

这是我到目前为止编写的代码,但现在的输出是: 你好 你好 你好 ... 世界 世界 世界 ...

错误/ s在哪里?谢谢。 :)

以下是代码:

class MyThread implements Runnable {
    private SimpleSyncThread sync;
    private String s;

    public MyThread(SimpleSyncThread sync, String s) {
        this.sync = sync;
        this.s = s;
    }

    public static void pause(long time) {
        try {Thread.sleep(time); }
        catch (InterruptedException e) {Thread.currentThread().interrupt();}
    }

    @Override
    public void run() {
        synchronized (sync) {
            for (int i = 0; i < 10; i++) {
                sync.print(s);
            }
        }
    }
}

public class SimpleSyncThread {

    public void print(String s) {
        System.out.println(s);
        MyThread.pause(200);
    }

    public static void main(String[] args) {
        SimpleSyncThread sync = new SimpleSyncThread();
        MyThread st1 = new MyThread(sync, "Hello");
        MyThread st2 = new MyThread(sync, "World");

        Thread t1 = new Thread(st1);
        Thread t2 = new Thread(st2);

        t1.start();
        t2.start();
    }
}

3 个答案:

答案 0 :(得分:2)

您在这里持有锁,因此一次只能打印一个进程

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/Widget"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">
 <ListView
    android:id="@+id/WidgetItem"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:divider="#D3D3D3"
    android:dividerHeight="1dp"/>


 <TextView
    android:id="@+id/empty_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/darker_gray"
    android:gravity="center"
    android:text="@string/emptytext"
    android:visibility="gone"/>
 </LinearLayout>

您可以使用

暂时释放锁定,而不是这样做
   synchronized (sync) {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
        }
    }

你可能想到的是我称之为Ping Pong测试。你不会在真正的程序中这样做,但这种模式是一个有用的微观基准。

   synchronized (sync) {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
            // I have done my bit, wake other threads.
            sync.notifyAll();
            try {
                // give up the lock and let another thread run.
                sync.wait(10);
            } catch(InterruptedException ie) {
                throw new AssertionError(ie);
            }
        }
    }

打印

public class PingPongMain {
    public static void main(String[] args) throws InterruptedException {
        boolean[] next = {false};
        AtomicInteger count = new AtomicInteger();
        Thread t1 = new Thread(() -> {
            try {
                synchronized (next) {
                    for(;;) {
                        // handle spurious wake ups.
                        while (next[0])
                            next.wait();

                        System.out.println("ping");

                        // state change before notify
                        next[0] = true;
                        next.notifyAll();
                    }
                }
            } catch (InterruptedException e) {
                // expected
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                synchronized (next) {
                    for(;;) {
                        // handle spurious wake ups.
                        while (!next[0])
                            next.wait();

                        System.out.println("pong");

                        // state change before notify
                        next[0] = false;
                        next.notifyAll();

                        count.incrementAndGet();
                    }
                }
            } catch (InterruptedException e) {
                // expected
            }
        });

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

        Thread.sleep(5000);
        t1.interrupt();
        t2.interrupt();
        System.out.println("Ping ponged " + count + " times in 5 seconds");

    }
}

答案 1 :(得分:1)

我想你想模拟一个有两个独立线程访问相同资源的作业:例如System.out,但睡眠部分可能同时运行。

在模拟中,您不应该将暂停放在同步块中:

public class SimpleSyncThread {

    public void print(String s) {
        synchronized(this){
           System.out.println(s);
        }
        MyThread.pause(200);
    }

在运行功能中,您不再需要同步:

public void run() {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
        }
}

现在,您将获得“Hello World Hello World”,或者“Hello World World Hello&#39;”

答案 2 :(得分:1)

你已经找到了一个非常小的难度的例子:通过waitnotify在java中的基本同步旨在同步消费者 - 生产者范例:有一些生产者线程和一些消费者线程。每个生产者在不等待的情况下完成其工作,然后唤醒(通知)正在等待通知的消费者线程。即使可运行的类同时是消费者和生产者,该方案也将起作用。但是通信总是单向

   producer -> consumer

相反,您尝试做的是双向替代线程通信:

   producer -> consumer -> producer -> consumer ...

我认为您需要一种更复杂的方式来传达您的线程:一个令牌管理器,一个包含从0到N的整数令牌的类,并将其旋转:

public class TokenManager
{
    private final int maxTokens;

    private int token;

    public TokenManager(int maxTokens, int initialToken)
    {
        super();
        this.maxTokens=maxTokens;
        this.token=initialToken % this.maxTokens;
    }

    public int getToken()
    {
        return this.token;
    }

    public void nextToken()
    {
        this.token=++this.token % this.maxTokens;
    }
}

然后是一个可运行的类,它接收TokenManager并将其用于同步:

public class MyRunnable implements Runnable
{
    private final String text;

    private final TokenManager token;

    // Identifier token value for this object.
    private final int id;

    public MyRunnable(int id, String text, TokenManager token)
    {
        super();
        this.id=id;
        this.text=text;
        this.token=token;
    }

    @Override
    public void run()
    {
        try
        {
            for (int i=0; i < 10; i++)
            {
                synchronized (this.token)
                {
                    // Wait until the TokenManager token is equal to the id of this object:
                    while (this.token.getToken() != this.id)
                    {
                        this.token.wait();
                    }

                    // Now it's our turn to print:
                    System.out.printf("%d: %s\n", i, this.text);
                    this.token.nextToken();

                    // Ask the TokenManager to progress to the next token:
                    this.token.notifyAll();
                }
            }
        }
        catch (InterruptedException e)
        {
            throw new Error(e);
        }
    }
}

public static void main(String[] args)
{
    // Instantiate the TokenManager for a specified number of threads:
    TokenManager token=new TokenManager(2, 0);

    // Instantiate and start the thread with id=0:
    new Thread(new MyRunnable(0, "Hello", token)).start();
    // Instantiate and start the thread with id=1:
    new Thread(new MyRunnable(1, "World", token)).start();
}

通过这种方式,主要方法是通过为实例化的线程分配ID(按升序)来决定激活顺序。而且,如果你想要一种新类型的线程,你只需要将3传递给TokenManager(而不是2)并启动一个具有正确ID的新线程:

    new Thread(new MyRunnable(2, "I'm here", token)).start();

备注(感谢Andy Brown):

  • 必须按顺序给出令牌ID,没有间隙。
  • 可能有多个具有相同令牌ID的线程。在这种情况下,它们将在轮流中随机执行。