在 Thread 上调用方法 - 它有效,但为什么呢?
Calling a method on a Thread - it works, but why?
我有以下 Thread 子类(为了便于阅读而稍微简化):
public class ConnectionHandlerThread extends Thread {
private Socket socket;
private volatile boolean disconnected = false;
private ObjectOutputStream out = null;
private ObjectInputStream in = null;
public ConnectionHandlerThread(Socket socket){
this.socket = socket;
}
public void run(){
disconnected = false;
try {
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
while(!disconnected){
try{
Object data = in.readObject();
}
catch(ClassNotFoundException e){
// Handle exception
}
catch(IOException e){
// Handle exception
}
}
}
catch (IOException e) {
// Handle exception
}
}
public void send(Object data){
try{
out.writeObject( data );
}
catch(IOException e){
// Handle exception
}
}
}
当我连接到服务器时,我的客户端(使用 Swing GUI)创建了这个线程的一个实例。我觉得奇怪的是我可以从主 GUI 调用方法 send(Object data)
,并且它有效。为什么无限 while 循环 and/or 调用 in.readObject()
不阻止我这样做?我的意思是线程不应该一直忙于做其他事情吗?这是做事的好方法吗?如果不是,为什么不呢?
编辑
澄清让我困惑的地方:如果这是在主线程中,它会忙于那个 in.readObject()
直到读取某些内容,然后它会在循环的下一次迭代中再次开始监听.当然,我知道我可以从另一个线程调用 send()
。但我的想法是 - "who" 实际上正在执行 send()
?是我的线程在做,还是调用线程在做?如果是前者,怎么会在执行send()
方法的while循环和中都忙着等待输入呢?我只是很难全神贯注...
有两件事:
1) 无限循环不会让 cpu 只忙于自身。它只是在它可用时一直保持忙碌,但我也使用它的其他线程。
2) 当你打电话给你的
send(Object data)
你不会在你的线程中这样做,所以请记住 1) 它被调用没有什么奇怪的
示例:
代码:
public class Main {
public static void main(String[] args) {
InfiniteThread t = new InfiniteThread();
t.start();
for(int i=0;i<10;i++) {
t.fromOut(i);
}
}
@DebugLog
public static class InfiniteThread extends Thread {
public void run() {
for(int i=0;i<10;i++) {
fromIn();
}
}
private void fromIn() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void fromOut(Object data){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出:
InfiniteThread :: ⇢ () [Thread:"main"] InfiniteThread :: ⇠
[0ms] InfiniteThread :: ⇢ run() [Thread:"Thread-0"]
InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇢
fromOut(data=0) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms]
InfiniteThread :: ⇢ fromOut(data=1) [Thread:"main"] InfiniteThread ::
⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢
fromOut(data=2) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms]
InfiniteThread :: ⇢ fromOut(data=3) [Thread:"main"] InfiniteThread ::
⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢
fromOut(data=4) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms]
InfiniteThread :: ⇢ fromOut(data=5) [Thread:"main"] InfiniteThread ::
⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢
fromOut(data=6) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms]
InfiniteThread :: ⇢ fromOut(data=7) [Thread:"main"] InfiniteThread ::
⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢
fromOut(data=8) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms]
InfiniteThread :: ⇢ fromOut(data=9) [Thread:"main"] InfiniteThread ::
⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇠ fromIn
[1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"]
InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn()
[Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread
:: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms]
InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠
fromIn [1000ms] InfiniteThread :: ⇠ run [10003ms]
这是有效的,因为在 SWING/GUI 你正在做类似的事情:
ConnectionHandlerThread myThread = ConnectionHandlerThread(new Socket());
现在 send()
方法是 public,因此您可以在执行
时从 GUI Class 调用它
myThread.send(data);
诀窍是你在线程中使用了一个套接字,这都是因为你在 class ConnectionHandlerThread
的构造函数中传递了一个套接字
@jameslarge Yes, I am confused right now...
Thread
对象不是线程:它是一种具有可用于配置、创建和与线程交互的方法的东西。你正在使用你的 ConnectionHandlerThread
来做所有这些,你也在使用它(通过覆盖 run()
方法)来定义线程所做的工作,你也在使用它(通过几个私有字段)来表示线程操作的连接状态。这是很多重载。
一位作者这样说,"It is easy to write a program that has too few objects. It's hard to write one that has too many objects."
我会重新编写您的代码以使用三个不同的对象来完成您的 ConnectionHandlerThread 现在所做的三个不同的工作:
public class Connection {
private Socket socket;
private volatile boolean connected = true;
private ObjectOutputStream out = null;
private ObjectInputStream in = null;
public Connection(Socket socket){
this.socket = socket;
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
}
public void send(Object data){
try{
out.writeObject( data );
}
catch(IOException e){
handleExceptionInSend(e);
}
}
public Object receive() {
try{
Object data = in.readObject();
}
catch(ClassNotFoundException e){
handleExceptionInReceive(e);
return NULL;
}
catch(IOException e){
handleExceptionInReceive(e);
return NULL;
}
return Object;
}
public boolean isConnected() {
return connected;
}
...
}
public class ConnectionListener implements Runnable {
private final connection;
public ConnectionRunner(Connection connection) {
this->connection = connection;
}
public void run(){
while(connection.isConnected()){
Object o = connection.receive();
if (o) {
doSomethingWith(o);
}
}
}
}
public class Whatever {
...
public void whateverelse( ) {
Socket socket = ...;
Connection connection = new Connection(socket);
ConnectionListener listener = new ConnectionListener(connection);
Thread connectionThread = new Thread(listener);
connectionThread.start();
...
}
...
}
connection
对象知道如何发送和接收数据。
listener
对象定义了线程的作用:即,它从连接接收对象直到 ! isConnected()
并用它们做一些事情。
最后,connectionThread
对象是启动线程的对象(可用于等待它完成等),如果需要的话。
它的代码比你写的要多,但是在我看来,它更容易理解,因为它更容易看到每个单独部分的职责。有时,尤其是当您与其他开发人员协作时,使代码易于理解比使其小巧更重要。
我有以下 Thread 子类(为了便于阅读而稍微简化):
public class ConnectionHandlerThread extends Thread {
private Socket socket;
private volatile boolean disconnected = false;
private ObjectOutputStream out = null;
private ObjectInputStream in = null;
public ConnectionHandlerThread(Socket socket){
this.socket = socket;
}
public void run(){
disconnected = false;
try {
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
while(!disconnected){
try{
Object data = in.readObject();
}
catch(ClassNotFoundException e){
// Handle exception
}
catch(IOException e){
// Handle exception
}
}
}
catch (IOException e) {
// Handle exception
}
}
public void send(Object data){
try{
out.writeObject( data );
}
catch(IOException e){
// Handle exception
}
}
}
当我连接到服务器时,我的客户端(使用 Swing GUI)创建了这个线程的一个实例。我觉得奇怪的是我可以从主 GUI 调用方法 send(Object data)
,并且它有效。为什么无限 while 循环 and/or 调用 in.readObject()
不阻止我这样做?我的意思是线程不应该一直忙于做其他事情吗?这是做事的好方法吗?如果不是,为什么不呢?
编辑
澄清让我困惑的地方:如果这是在主线程中,它会忙于那个 in.readObject()
直到读取某些内容,然后它会在循环的下一次迭代中再次开始监听.当然,我知道我可以从另一个线程调用 send()
。但我的想法是 - "who" 实际上正在执行 send()
?是我的线程在做,还是调用线程在做?如果是前者,怎么会在执行send()
方法的while循环和中都忙着等待输入呢?我只是很难全神贯注...
有两件事:
1) 无限循环不会让 cpu 只忙于自身。它只是在它可用时一直保持忙碌,但我也使用它的其他线程。
2) 当你打电话给你的
send(Object data)
你不会在你的线程中这样做,所以请记住 1) 它被调用没有什么奇怪的
示例:
代码:
public class Main {
public static void main(String[] args) {
InfiniteThread t = new InfiniteThread();
t.start();
for(int i=0;i<10;i++) {
t.fromOut(i);
}
}
@DebugLog
public static class InfiniteThread extends Thread {
public void run() {
for(int i=0;i<10;i++) {
fromIn();
}
}
private void fromIn() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void fromOut(Object data){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出:
InfiniteThread :: ⇢ () [Thread:"main"] InfiniteThread :: ⇠ [0ms] InfiniteThread :: ⇢ run() [Thread:"Thread-0"] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇢ fromOut(data=0) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=1) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=2) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=3) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=4) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=5) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=6) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=7) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=8) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=9) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇠ run [10003ms]
这是有效的,因为在 SWING/GUI 你正在做类似的事情:
ConnectionHandlerThread myThread = ConnectionHandlerThread(new Socket());
现在 send()
方法是 public,因此您可以在执行
myThread.send(data);
诀窍是你在线程中使用了一个套接字,这都是因为你在 class ConnectionHandlerThread
@jameslarge Yes, I am confused right now...
Thread
对象不是线程:它是一种具有可用于配置、创建和与线程交互的方法的东西。你正在使用你的 ConnectionHandlerThread
来做所有这些,你也在使用它(通过覆盖 run()
方法)来定义线程所做的工作,你也在使用它(通过几个私有字段)来表示线程操作的连接状态。这是很多重载。
一位作者这样说,"It is easy to write a program that has too few objects. It's hard to write one that has too many objects."
我会重新编写您的代码以使用三个不同的对象来完成您的 ConnectionHandlerThread 现在所做的三个不同的工作:
public class Connection {
private Socket socket;
private volatile boolean connected = true;
private ObjectOutputStream out = null;
private ObjectInputStream in = null;
public Connection(Socket socket){
this.socket = socket;
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
}
public void send(Object data){
try{
out.writeObject( data );
}
catch(IOException e){
handleExceptionInSend(e);
}
}
public Object receive() {
try{
Object data = in.readObject();
}
catch(ClassNotFoundException e){
handleExceptionInReceive(e);
return NULL;
}
catch(IOException e){
handleExceptionInReceive(e);
return NULL;
}
return Object;
}
public boolean isConnected() {
return connected;
}
...
}
public class ConnectionListener implements Runnable {
private final connection;
public ConnectionRunner(Connection connection) {
this->connection = connection;
}
public void run(){
while(connection.isConnected()){
Object o = connection.receive();
if (o) {
doSomethingWith(o);
}
}
}
}
public class Whatever {
...
public void whateverelse( ) {
Socket socket = ...;
Connection connection = new Connection(socket);
ConnectionListener listener = new ConnectionListener(connection);
Thread connectionThread = new Thread(listener);
connectionThread.start();
...
}
...
}
connection
对象知道如何发送和接收数据。
listener
对象定义了线程的作用:即,它从连接接收对象直到 ! isConnected()
并用它们做一些事情。
最后,connectionThread
对象是启动线程的对象(可用于等待它完成等),如果需要的话。
它的代码比你写的要多,但是在我看来,它更容易理解,因为它更容易看到每个单独部分的职责。有时,尤其是当您与其他开发人员协作时,使代码易于理解比使其小巧更重要。