Heim  >  Artikel  >  Java  >  Was ist die Methode zum Kopieren von Java-Multithread-Haltepunkten?

Was ist die Methode zum Kopieren von Java-Multithread-Haltepunkten?

WBOY
WBOYnach vorne
2023-04-13 17:31:19628Durchsuche

Einführung in die Details

Ich verwende eine Timer-Klasse (java.util.Timer), um die Haltepunktfunktion zu implementieren. Der aufgezeichnete Inhalt ist der Fortschritt der Replikation jedes Threads.

Einführung in die Timer-Klasse:

Eine Möglichkeit für Threads, Aufgaben für die zukünftige Ausführung in einem zu planen Hintergrundthread kann zur einmaligen Ausführung oder zur wiederholten Ausführung in regelmäßigen Abständen geplant werden. Thread Eine Funktion, die Aufgaben in einem Hintergrundthread für die zukünftige Ausführung plant. Aufgaben können so geplant werden, dass sie einmal ausgeführt werden oder sich regelmäßig wiederholen.

Gemäß der Einführung in die API ist ersichtlich, dass diese Timer-Klasse Aufgaben nur einmal oder periodisch ausführen kann. (Beachten Sie, dass es sich bei dieser Klasse um die Klasse java.util.Timer handelt, nicht um die Klasse im Javax-Paket.)

Diese Klasse verfügt über viele zeitbezogene Methoden, die hier nicht vorgestellt werden Interessierte können gehen. Verstehen Sie, dass wir hier nur eine Methode vorstellen, die wir verwenden müssen.

public void schedule(TimerTask task, long delay, long period)

Plant die angegebene Aufgabe für die wiederholte Ausführung mit fester Verzögerung, beginnend nach der angegebenen Verzögerung. Nachfolgende Ausführungen finden in ungefähr regelmäßigen Abständen statt, getrennt durch den angegebenen Zeitraum -Verzögerung der Ausführung, die nach einer angegebenen Verzögerung beginnt. Nachfolgende Ausführungen erfolgen in ungefähren Abständen in bestimmten Abständen.

Verwenden Sie diese Methode, um die Kopierfortschrittsinformationen jedes Threads in einem festen Zeitintervall aufzuzeichnen. #🎜🎜 ## 🎜🎜#Code Teil#🎜🎜 ## 🎜🎜##geplante Aufgabenklasse
package dragon.local;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class RecordTask extends TimerTask {
public static final String filename = "breakPointRecord.txt";
private Timer timer;
private List<FileCopyThread> copyThreads;
private String outputPath;

public RecordTask(Timer timer, List<FileCopyThread> copyThreads, String outputPath) {
this.timer = timer;
this.copyThreads = copyThreads;
this.outputPath = outputPath;
}

@Override
public void run() {
try {
this.breakPointRecord();
} catch (IOException e) {
e.printStackTrace();
}
}

public void breakPointRecord() throws FileNotFoundException, IOException {
int aliveThreadNum = 0;  //存活线程数目
//不使用追加方式,这里只需要最新的记录即可。
File recordFile = new File(outputPath, filename);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(recordFile))){
//每次记录一个线程的下载位置,但是取出来又需要进行转换,太麻烦了。
//我们直接使用序列化来进行操作,哈哈!
long[] curlen = new long[4];
int index = 0;
for (FileCopyThread copyThread : copyThreads) {
if (copyThread.isAlive()) {
aliveThreadNum++;
}
curlen[index++] = copyThread.getCurlen();
System.out.println(index+" curlen: "+copyThread.getCurlen());
}
//创建 Record 对象,并序列化。
oos.writeObject(new Record(curlen));
}
//当所有的线程都死亡时,关闭计时器,删除记录文件。(所有线程死亡的话,就是文件已经复制完成了!)
if (aliveThreadNum == 0) {
timer.cancel();
recordFile.delete();
}
System.out.println("线程数量: "+aliveThreadNum);
}
}
#🎜🎜 ## 🎜🎜#Beschreibung:#🎜🎜 ## 🎜🎜#
if (aliveThreadNum == 0) {
timer.cancel();
recordFile.delete();
}

#wenn Wenn alle Threads beendet sind, bedeutet dies, dass das Programm normal beendet wurde.

Die Aufnahmedatei wird zu diesem Zeitpunkt gelöscht. Die Datensatzdatei ist hier ein Flag. Wenn die Datensatzdatei vorhanden ist, bedeutet dies, dass das Programm nicht normal beendet wurde. Beim erneuten Start wird eine Haltepunktkopie

durchgeführt.

Hinweis: IO-Ausnahmen während des Kopiervorgangs werden hier nicht berücksichtigt. Wenn der Thread eine IO-Ausnahme auslöst, wird auch der Status des Threads beendet. Angesichts der Tatsache, dass es beim lokalen Kopieren von Dateien immer noch relativ wenige E/A-Ausnahmen gibt, habe ich nicht berücksichtigt, dass die Funktion dieses Programms möglicherweise verbessert werden muss, wenn es aus dem Internet heruntergeladen wird.

Informationsklasse aufzeichnen

Jedes Mal müssen die Informationen jedes Threads nacheinander geschrieben werden, sie müssen jedoch nach dem Auslesen noch konvertiert werden. Das fühlt sich immer noch zu mühsam an. Nutzen Sie hier direkt den Serialisierungsmechanismus von Java.

Manchmal ist es praktisch, Objekte direkt zu manipulieren. Hinweis: Der Index des Arrays stellt die Position jedes Threads dar.

package dragon.local;

import java.io.Serializable;

public class Record implements Serializable{
/**
 * 序列化 id
 */
private static final long serialVersionUID = 1L;
private long[] curlen;

public Record(long[] curlen) {
this.curlen = curlen;
} 

public long[] getCurlen() {
return this.curlen;
}
}

Thread-Klasse kopieren

package dragon.local;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class FileCopyThread extends Thread {
private int index;
private long position;
private long size;
private File targetFile;
private File outputFile;
private long curlen;      //当前下载的长度

public FileCopyThread(int index, long position, long size, File targetFile, File outputFile) {
this.index = index;
this.position = position;
this.size = size;
this.targetFile = targetFile;
this.outputFile = outputFile;
this.curlen = 0L;
}

@Override
public void run() {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(targetFile));
RandomAccessFile raf = new RandomAccessFile(outputFile, "rw")){
bis.skip(position);  //跳过不需要读取的字节数,注意只能先后跳
raf.seek(position);  //跳到需要写入的位置,没有这句话,会出错,但是很难改。
int hasRead = 0;
byte[] b = new byte[1024];
/**
 * 注意,每个线程只是读取一部分数据,不能只以 -1 作为循环结束的条件
 * 循环退出条件应该是两个,即写入的字节数大于需要读取的字节数 或者 文件读取结束(最后一个线程读取到文件末尾)
 */
while(curlen < size && (hasRead = bis.read(b)) != -1) {
raf.write(b, 0, hasRead);
curlen += (long)hasRead;
//强制停止程序。
//if (curlen > 17_000_000) {
//System.exit(0);
//}
}

System.out.println(index+" "+position+" "+curlen+" "+size);
} catch (IOException e) {
e.printStackTrace();
}
}

public long getCurlen() {   //获取当前的进度,用于记录,以便必要时恢复读取进度。
return position+this.curlen;
}
}

Dieser Code wird kopiert, um Haltepunkte zu testen. Wenn Sie einen Test durchführen möchten, können Sie die Bedingungen in der if-Beurteilung entsprechend der Größe der zu kopierenden Datei anpassen. Wenn Sie testen möchten, können Sie diesen Code auskommentieren, bevor Sie das Programm ausführen (dann wird das Programm beendet und die Datei wird zu diesem Zeitpunkt nicht kopiert). Anschließend können Sie diesen Code auskommentieren und das Programm erneut ausführen. Die Datei wird dann kopiert erfolgreich.

//强制停止程序。
//if (curlen > 17_000_000) {
//System.exit(0);
//}

Kopieren-Tool-Klasse

package dragon.local;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;



/**
 * 设计思路:
 * 获取目标文件的大小,然后设置复制文件的大小(这样做是有好处的),
 * 然后使用将文件分为 n 分,使用 n 个线程同时进行复制(这里我将 n 取为 4)。
 * 
 * 进一步拓展:
 * 加强为断点复制功能,即程序中断以后,
 * 仍然可以继续从上次位置恢复复制,减少不必要的重复开销
 * */

public class FileCopyUtil {
//设置一个常量,复制线程的数量
private static final int THREAD_NUM = 4;

private FileCopyUtil() {}

/**
 * @param targetPath 目标文件的路径
 * @param outputPath 复制输出文件的路径
 * @throws IOException 
 * @throws ClassNotFoundException 
 * */
public static void transferFile(String targetPath, String outputPath) throws IOException, ClassNotFoundException {
File targetFile = new File(targetPath);
File outputFilePath = new File(outputPath);
if (!targetFile.exists() || targetFile.isDirectory()) {   //目标文件不存在,或者是一个文件夹,则抛出异常
throw new FileNotFoundException("目标文件不存在:"+targetPath);
}
if (!outputFilePath.exists()) {     //如果输出文件夹不存在,将会尝试创建,创建失败,则抛出异常。
if(!outputFilePath.mkdir()) {
throw new FileNotFoundException("无法创建输出文件:"+outputPath);
}
}

long len = targetFile.length();

File outputFile = new File(outputFilePath, "copy"+targetFile.getName());
createOutputFile(outputFile, len);    //创建输出文件,设置好大小。

//创建计时器 Timer 对象
Timer timer = new Timer();

long[] position = new long[4];
//每一个线程需要复制文件的起点
long size = len / FileCopyUtil.THREAD_NUM + 1;     //保存复制线程的集合
List<FileCopyThread> copyThreads = new ArrayList<>();
Record record = getRecord(outputPath);

for (int i = 0; i < FileCopyUtil.THREAD_NUM; i++) {
//如果已经有了 记录文件,就从使用记录数据,否则就是新的下载。
position[i] = record == null ? i*size : record.getCurlen()[i];
FileCopyThread copyThread = new FileCopyThread(i, position[i], size, targetFile, outputFile);
copyThread.start();     //启动复制线程
copyThreads.add(copyThread);   //将复制线程添加到集合中。
}

timer.schedule(new RecordTask(timer, copyThreads, outputPath), 0L, 100L);  //立即启动计时器,每隔10秒记录一次位置。
System.out.println("开始了!");
}

//创建输出文件,设置好大小。
private static void createOutputFile(File file, long length) throws IOException {
try (   
RandomAccessFile raf = new RandomAccessFile(file, "rw")){
raf.setLength(length);
}
}

//获取以及下载的位置
private static Record getRecord(String outputPath) throws FileNotFoundException, IOException, ClassNotFoundException {
File recordFile = new File(outputPath, RecordTask.filename);
if (recordFile.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(recordFile))){
return (Record) ois.readObject();
}
}
return null;
}
}
Beschreibung: Bestimmen Sie, ob das Kopieren von Haltepunkten gestartet werden soll, basierend darauf, ob sich im kopierten Verzeichnis eine Datensatzdatei befindet.

private static Record getRecord(String outputPath) throws FileNotFoundException, IOException, ClassNotFoundException {
File recordFile = new File(outputPath, RecordTask.filename);
if (recordFile.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(recordFile))){
return (Record) ois.readObject();
}
}
return null;
}

Das Starten der Haltepunktkopie erweist sich als sehr einfach, genau wie das Kopieren, mit der Ausnahme, dass die Startposition der Kopie zur aufgezeichneten Position wird.

//如果已经有了 记录文件,就从使用记录数据,否则就是新的下载。
position[i] = record == null ? i*size : record.getCurlen()[i];

Das obige ist der detaillierte Inhalt vonWas ist die Methode zum Kopieren von Java-Multithread-Haltepunkten?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen