我正在尝试用Java实现相互通信的节点。我这样做是为每个想要与服务器通信的节点创建一个新线程。
当给定数量的节点(即已创建多个线程)连接到服务器时,我希望每个线程在添加到“sharedCounter”后执行下一位代码。
我认为我需要在共享变量上使用'locks',以及signalAll()或notifyAll()之类的东西来获取所有线程,但我似乎无法清楚地知道它是如何工作的实施它。
任何解释这些Java概念的帮助都将不胜感激:D
下面是我的代码的结构:
import java.net.*;
import java.io.*;
public class Node {
public static void main(String[] args) {
...
// Chooses server or client launchers depend on parameters.
...
}
}
class sharedResource {
private int sharedCounter;
public sharedResource(int i) {
sharedCounter = i;
}
public synchronized void incSharedCounter() {
sharedCounter--;
if (sharedCounter == 0)
// Get all threads to do something
}
}
class Server {
...
for (int i = 0; i < numberOfThreads; i++) {
new serverThread(serverSocket.accept()).start();
}
...
sharedResource threadCount = new sharedResource(numberOfThreads);
...
}
class serverThread extends Thread {
...
//some code
Server.threadCount.incSharedCounter();
// Some more code to run when sharedCounte == 0
...
}
class Client {
...
}
答案 0 :(得分:3)
//获取所有线程
线程(或者说Runnables,你应该实现而不是扩展Thread)有一个run方法,它包含了它们应该执行的代码。
一旦你调用Thread #start(后来调用Runnable#run),线程就会开始这样做。
由于您似乎不熟悉Java中的多线程,因此我建议您阅读已在Java5中引入的并发实用程序包的简介,以便更轻松地实现并发操作。
具体而言,您似乎正在寻找的是一种“暂停”操作直到满足条件的方法(在您的情况下,计数器已达到零)。为此,您应该查看CountDownLatch。
答案 1 :(得分:1)
事实上,这个主题很广泛,但我会尝试解释基础知识。可以从各种blogs和文章中阅读更多详细信息。其中一个是Java trail。
最好将每个线程看作是在比赛中彼此并排的跑步者(自然人)。每个跑步者可以在跑步时执行任何任务。例如,在比赛的特定时刻从桌子上取一杯水。在物理上,他们不能同时从同一个杯子喝水,但在虚拟世界中,它是可能的(这是画线的地方)。
例如,再拿两个跑步者;他们每个人都必须在一个轨道上来回奔跑,并在每一端按下按钮(由跑步者共享)1000,000次,按钮只是每次递增一个计数器。当他们完成运行时,计数器的价值是多少?在物理世界中,它将是2'000'000,因为跑步者不能同时按下按钮,他们会等待第一个先离开...这是除非他们为此而战...嗯,这正是两个线程会做的事情。请考虑以下代码:
public class ThreadTest extends Thread {
static public final int TOTAL_INC = 1000000;
static public int counter = 0;
@Override
public void run() {
for (int i=0; i<TOTAL_INC; i++) {
counter++;
}
System.out.println("Thread stopped incrementing counter " + TOTAL_INC + " times");
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadTest();
Thread t2 = new ThreadTest();
t1.start();
t2.start();
t1.join(); // wait for each thread to stop on their own...
t2.join(); //
System.out.println("Final counter is : " + counter + " which should be equal to " + TOTAL_INC * 2);
}
}
输出可能类似于
Thread stopped incrementing counter 1000000 times
Thread stopped incrementing counter 1000000 times
Final counter is : 1143470 which should be equal to 2000000
偶尔,两个线程只会增加相同的值两次;这被称为竞争条件。
同步run
方法不起作用,您必须使用一些锁定机制来防止这种情况发生。请考虑run
方法中的以下更改:
static private Object lock = new Object();
@Override
public void run() {
for (int i=0; i<TOTAL_INC; i++) {
synchronized(lock) {
counter++;
}
}
System.out.println("Thread stopped incrementing counter " + TOTAL_INC + " times");
}
现在预期的输出是
...
Final counter is : 2000000 which should be equal to 2000000
我们已将计数器与共享对象同步。这就像在只有一个跑步者可以一次访问按钮之前放置队列一行。
注意:此锁定机制称为mutex。如果一次可以通过 n 线程访问资源,您可以考虑使用semaphore。
多线程也与死锁有关。 deadlock是两个线程相互等待另一个线程以释放某些同步资源以继续。例如:
虽然有很多方法可以防止这种情况发生(这取决于你的线程在做什么,以及它们是如何实现的......)你应该特别阅读它。
注意:方法wait
,notify
和notifyAll
只能在同步对象时调用。例如:
static public final int TOTAL_INC = 10;
static private int counter = 0;
static private Object lock = new Object();
static class Thread1 extends Thread {
@Override
public void run() {
synchronized (lock) {
for (int i=0; i<TOTAL_INC; i++) {
try {
lock.wait();
counter++;
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static class Thread2 extends Thread {
@Override
public void run() {
synchronized (lock) {
for (int i=0; i<TOTAL_INC; i++) {
try {
lock.notify();
counter--;
lock.wait();
} catch (InterruptedException e) {
/* ignored */
}
}
}
}
}
请注意,两个线程都在synchronized块中运行for...loop
块。 (两个线程结束时counter == 0
的结果。)这可以实现,因为它们“让对方”通过资源的wait
和notify
方法访问同步资源。不使用这两种方法,两个线程都只是顺序运行而不是同时运行(或更准确地说,交替运行)。
我希望这能够揭示线程(在Java中)。
** 更新 **
以下是使用CountDownLatch之前Thilo类建议的{{3}}类,对上述所有内容的概念进行了一些证明:
static class Server {
static public final int NODE_COUNT = 5;
private List<RunnableNode> nodes;
private CountDownLatch startSignal;
private Object lock = new Object();
public Server() {
nodes = Collections.synchronizedList(new ArrayList<RunnableNode>());
startSignal = new CountDownLatch(Server.NODE_COUNT);
}
public Object getLock() {
return lock;
}
public synchronized void connect(RunnableNode node) {
if (startSignal.getCount() > 0) {
startSignal.countDown();
nodes.add(node);
System.out.println("Received connection from node " + node.getId() + " (" + startSignal.getCount() + " remaining...)");
} else {
System.out.println("Client overflow! Refusing connection from node " + node.getId());
throw new IllegalStateException("Too many nodes connected");
}
}
public void shutdown() {
for (RunnableNode node : nodes) {
node.shutdown();
}
}
public void awaitAllConnections() {
try {
startSignal.await();
synchronized (lock) {
lock.notifyAll(); // awake all nodes
}
} catch (InterruptedException e) {
/* ignore */
shutdown(); // properly close any connected node now
}
}
}
static class RunnableNode implements Runnable {
private Server server;
private int id;
private boolean working;
public RunnableNode(int id, Server server) {
this.id = id;
this.server = server;
this.working = true;
}
public int getId() {
return id;
}
public void run() {
try {
Thread.sleep((long) (Math.random() * 5) * 1000); // just wait randomly from 0 to 5 seconds....
synchronized (server.getLock()) {
server.connect(this);
server.getLock().wait();
}
if (!Thread.currentThread().isAlive()) {
throw new InterruptedException();
} else {
System.out.println("Node " + id + " started successfully!");
while (working) {
Thread.yield();
}
}
} catch (InterruptedException e1) {
System.out.print("Ooop! ...");
} catch (IllegalStateException e2) {
System.out.print("Awwww! Too late! ...");
}
System.out.println("Node " + id + " is shutting down");
}
public void shutdown() {
working = false; // shutdown node here...
}
}
static public void main(String...args) throws InterruptedException {
Server server = new Server();
for (int i=0; i<Server.NODE_COUNT + 4; i++) { // create 4 more nodes than needed...
new Thread(new RunnableNode(i, server)).start();
}
server.awaitAllConnections();
System.out.println("All connection received! Server started!");
Thread.sleep(6000);
server.shutdown();
}
答案 2 :(得分:0)
这是一个广泛的主题。您可以尝试通过official guides阅读Java中的并发(即线程,或多或少)。这不是切割和干燥的解决方案;你必须设计一些东西。