[英]Hello World with Multithreading Java
我想了解如何使用关键字:等待,通知/全部,已同步,因此我决定尝试一个简单的示例。 基本上我想做的是创建两个将打印字符串的线程。 第一个线程的字符串为“ Hello”,第二个线程的字符串为“ World”。
我想要的输出如下:Hello World Hello World Hello World ...
这是我到目前为止编写的代码,但是现在的输出是:Hello Hello Hello ...世界世界世界...
错误在哪里? 谢谢。 :)
这是代码:
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();
}
}
您在这里拿着锁,所以一次只能打印一个进程
synchronized (sync) {
for (int i = 0; i < 10; i++) {
sync.print(s);
}
}
您可以使用以下方法暂时解除锁定:
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");
}
}
版画
ping
pong
ping
pong
.. deleted ...
Ping ponged 323596 times in 5 seconds
我猜您想用两个访问相同资源的独立线程来模拟一个作业:例如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”。
您已经完成了一个具有不小的困难的示例:java中通过wait
and notify
的基本同步旨在同步使用者-生产者范例:有一些生产者线程和一些使用者线程。 每个生产者无需等待便完成其工作,然后唤醒(通知)正在等待通知的使用者线程。 即使可运行类同时是消费者和生产者,此方案也将起作用。 但是通信始终是单向的 :
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();
注意事项(感谢安迪·布朗 ):
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.