java Java线程序列化,为什么可以启动序列化的线程对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10622438/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 01:52:49  来源:igfitidea点击:

Java Thread Serialization , why serialized Thread Object can be started

javaserialization

提问by Abhishek Choudhary

A thread in java can't be restarted in Java, so I implemented a java Thread , and then tried to restart the thread after getting the serialized object of Thread.

java中的一个线程在java中是无法重启的,所以我实现了一个java Thread ,然后在拿到Thread的序列化对象后尝试重启线程。

import java.io.Serializable;

public class ThreadSerialization extends Thread implements Serializable {

    int iCheck = 10;
    @Override
    public void run() {
        System.out.println("STARTING");
        for(int i=0;i<10;i++){
            iCheck+=i;
        }
    }

}

and Serializing algo-

和序列化算法-

public class CallingThreadSerializable {

    public static void main(String[] args) {
        ThreadSerialization ser = new ThreadSerialization();
        ser.start();
        FileOutputStream fos = null;
        ObjectOutputStream out = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fos = new FileOutputStream("thread.ser");
            out = new ObjectOutputStream(fos);
            out.writeObject(ser);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        try {
            fis = new FileInputStream("thread.ser");
            ois = new ObjectInputStream(fis);
            ThreadSerialization ser1 = (ThreadSerialization) ois.readObject();
            System.out.println("---> " + ser1.iCheck);
            ser1.start();
            System.out.println("---> " + ser1.iCheck);
            ois.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

OUTPUT-

输出-

STARTING
---> 55
---> 55
STARTING

Why the ser1 object is starting again ?

为什么 ser1 对象又开始了?

回答by A.H.

There are two points:

有两点:

First: Threadis NOT Serializableand hence the following excerpt from the serializableJavaDoc applies:

第一:Thread不是Serializable,因此serializableJavaDoc的以下摘录适用:

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields.

为了允许不可序列化的类的子类型被序列化,子类型可能负责保存和恢复超类型的公共、受保护和(如果可访问)包字段的状态。

Which mean, your class ThreadSerializationwould have the responsibility to store and restore the state of Thread. But you cannot do that due to many privatefields in Thread. Therefore all private field in Threadare default initialized. Now look at implementation of Thread.start():

这意味着,您的班级ThreadSerialization将负责存储和恢复Thread. 但是,你不能这样做,因为很多private的领域Thread。因此所有私有字段Thread都默认初始化。现在看看实现Thread.start()

    //...
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    // ...
    start0();
    //...

Because threadStatushas not been stored/restored properly you can start a second time.

由于threadStatus未正确存储/恢复,您可以再次启动。

Second: Don't confuse the actual operating system thread and the "manager" object java.lang.Thread- they are only loosely coupled. In your example you only serialize the manager but not the OS-thread which has no representation in Java. After deserialization you have a second manager instance which noattached OS-thread. So telling the manager to start will succeed.

第二:不要混淆实际的操作系统线程和“管理器”对象java.lang.Thread——它们只是松散耦合的。在您的示例中,您只序列化了管理器,而不是在 Java 中没有表示的操作系统线程。反序列化后,您有第二个管理器实例,它没有附加操作系统线程。所以告诉经理开始会成功。

回答by Jivings

If you deserialize(?) an Object then you are essentially creating a new instance of that class with the same properties as the original Object.

如果您反序列化(?)一个对象,那么您实际上是在创建该类的新实例,该实例具有与原始对象相同的属性。

It is not the same Object.

它不是同一个对象。

As well, since Thread itself doesn't implement Serializable, startedness is effectively part of the transient state in any Serializable subclass (as is interruptedness), because serialization doesn't restore fields inherited from a non-Serializable superclass. Any class that extends Thread implements Serializable, assuming there's a good reason to do so at all, should probably have its own fields to track startedness and interruptedness, and call Thread.start()and Thread.interrupt()in readObject()or readResolve()to restore those elements of its state.

同样,由于 Thread 本身不实现 Serializable,因此启动性实际上是任何 Serializable 子类中瞬态状态的一部分(如中断),因为序列化不会恢复从非 Serializable 超类继承的字段。任何类extends Thread implements Serializable,假设有一个很好的理由都这样做,也许应该有自己的领域来跟踪startedness和interruptedness和呼叫Thread.start(),并Thread.interrupt()readObject()readResolve()恢复其状态的那些元素。

回答by Kumar Vivek Mitra

During serialization the Objects instance variables are sucked out, and converted to bytes and written on a file (typically a file with .ser extension) then, during deserialization the bytes are read from the file, and then converted to instance variables, and are used to create new object identical to the one which was serialized. Their constructor are never called, so it reaches the state when it was serialized not when it was created.

在序列化期间,Objects 实例变量被吸出,并转换为字节并写入文件(通常是具有 .ser 扩展名的文件)然后,在反序列化期间,字节从文件中读取,然后转换为实例变量,并被使用创建与序列化对象相同的新对象。它们的构造函数永远不会被调用,因此它在序列化时而不是在创建时达到状态。

You can check the hashcode of the object before serialization, and then after deserialization, they will never be same, as hashcode is provided by JVM depending on where the object is create on the heap, and its obvious the 2 objects cant be created on the heap at the same address...so they are diff objects but with identical state.

您可以在序列化之前检查对象的哈希码,然后在反序列化之后,它们将永远不会相同,因为哈希码由 JVM 提供,具体取决于对象在堆上的创建位置,很明显,无法在堆上创建 2 个对象堆在相同的地址......所以它们是不同的对象,但具有相同的状态。

回答by Gobi Shanmugam

To make an object serializable, the respective class should explicitly implement Serializable interface. However certain system classes defined by java like "Thread", "OutputStream", "Socket" are not serializable. Why so? Lets take a step back - now what is the use of serializing the Thread running in System1 JVM using System1 memory and then deserializing it in System2 and trying to run in System2 JVM. Makes sense right! Hence these classes are not serializable.

要使对象可序列化,相应的类应显式实现 Serializable 接口。然而,某些由 java 定义的系统类如“Thread”、“OutputStream”、“Socket”是不可序列化的。为什么这样?让我们退后一步 - 现在使用 System1 内存序列化在 System1 JVM 中运行的线程,然后在 System2 中反序列化它并尝试在 System2 JVM 中运行有什么用。有道理!因此这些类是不可序列化的。

Coming to your program.

来到你的节目。

ThreadSerialization ser1 = (ThreadSerialization) ois.readObject();// Thread started in System2.
ser1.start();// Thread once again started here in System2.

It is very bad to have a class which implements serializable and extends Thread or implemeting Runnable. In Summary Don't use thread in sealization or stream and socket process.

拥有一个实现可序列化并扩展 Thread 或实现 Runnable 的类是非常糟糕的。总结 不要在密封或流和套接字过程中使用线程。

回答by ceph3us

regardless of any other responses in this "thread" :)

无论此“线程”中的任何其他响应如何:)

Thread class cannot be serialized due to call to Native Methods!!!

由于调用了Native Methods,线程类无法序列化!!!

period!

时期!

EDIT:

编辑:

if you are working within a single process, you can declare a global variable. If you create multiple threads, you can create a class that will contain a list of active threads.

如果您在单个进程中工作,则可以声明一个全局变量。如果创建多个线程,则可以创建一个包含活动线程列表的类。

  • This class contains the hash map.
  • Hash map contains a key (id) as String and value as weak reference to thread.
  • Threads are identified by id
  • You do not have to worry about GC If your thread finishes or finish it yourself class method returns null.
  • 此类包含哈希映射。
  • 哈希映射包含一个键(id)作为字符串和值作为对线程的弱引用。
  • 线程由 id 标识
  • 您不必担心 GC 如果您的线程完成或自己完成类方法返回 null。


import java.lang.ref.WeakReference;
import java.util.HashMap;

public class ThreadsMap {

    private HashMap<String, WeakReference<Thread>> _threadHashMap;

    public ThreadsMap() {
        _threadHashMap = new HashMap<>();
    }

    public void add(String id, Thread thread) {
        WeakReference<Thread> threadWeakReference = new WeakReference<>(thread);
        _threadHashMap.put(id, threadWeakReference);
    }

    public Thread get(String id) {
        WeakReference<Thread> threadWeakReference = _threadHashMap.get(id);
        return threadWeakReference.get();
    }

}