Java 实现 FTP 服务

1、FTP简介
FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:“下载”(Download)和"上传"(Upload)。"下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。

实现FTP文件上传与下载的两种方式:

  • 1、通过JDK自带的API实现
  • 2、通过Apache提供的API实现

代码如下:

package com.cloudpower.util;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;import sun.net.TelnetInputStream;
import sun.net.TelnetOutputStream;
import sun.net.ftp.FtpClient;/*** Java自带的API对FTP的操作* * @Title:Ftp.java* @author: 周玲斌*/
public class Ftp {/*** 本地文件名*/private String localfilename;/*** 远程文件名*/private String remotefilename;/*** FTP客户端*/private FtpClient ftpClient;/*** 服务器连接* * @param ip*            服务器IP* @param port*            服务器端口* @param user*            用户名* @param password*            密码* @param path*            服务器路径* @author 周玲斌* @date 2012-7-11*/public void connectServer(String ip, int port, String user,String password, String path) {try {/* ******连接服务器的两种方法****** */// 第一种方法// ftpClient = new FtpClient();// ftpClient.openServer(ip, port);// 第二种方法ftpClient = new FtpClient(ip);ftpClient.login(user, password);// 设置成2进制传输ftpClient.binary();System.out.println("login success!");if (path.length() != 0) {// 把远程系统上的目录切换到参数path所指定的目录ftpClient.cd(path);}ftpClient.binary();} catch (IOException ex) {ex.printStackTrace();throw new RuntimeException(ex);}}/*** 关闭连接* * @author 周玲斌* @date 2012-7-11*/public void closeConnect() {try {ftpClient.closeServer();System.out.println("disconnect success");} catch (IOException ex) {System.out.println("not disconnect");ex.printStackTrace();throw new RuntimeException(ex);}}/*** 上传文件* * @param localFile*            本地文件* @param remoteFile*            远程文件* @author 周玲斌* @date 2012-7-11*/public void upload(String localFile, String remoteFile) {this.localfilename = localFile;this.remotefilename = remoteFile;TelnetOutputStream os = null;FileInputStream is = null;try {// 将远程文件加入输出流中os = ftpClient.put(this.remotefilename);// 获取本地文件的输入流File file_in = new File(this.localfilename);is = new FileInputStream(file_in);// 创建一个缓冲区byte[] bytes = new byte[1024];int c;while ((c = is.read(bytes)) != -1) {os.write(bytes, 0, c);}System.out.println("upload success");} catch (IOException ex) {System.out.println("not upload");ex.printStackTrace();throw new RuntimeException(ex);} finally {try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();} finally {try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}}/*** 下载文件* * @param remoteFile*            远程文件路径(服务器端)* @param localFile*            本地文件路径(客户端)* @author 周玲斌* @date 2012-7-11*/public void download(String remoteFile, String localFile) {TelnetInputStream is = null;FileOutputStream os = null;try {// 获取远程机器上的文件filename,借助TelnetInputStream把该文件传送到本地。is = ftpClient.get(remoteFile);File file_in = new File(localFile);os = new FileOutputStream(file_in);byte[] bytes = new byte[1024];int c;while ((c = is.read(bytes)) != -1) {os.write(bytes, 0, c);}System.out.println("download success");} catch (IOException ex) {System.out.println("not download");ex.printStackTrace();throw new RuntimeException(ex);} finally {try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();} finally {try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}}public static void main(String agrs[]) {String filepath[] = { "/temp/aa.txt", "/temp/regist.log" };String localfilepath[] = { "C:\\tmp\\1.txt", "C:\\tmp\\2.log" };Ftp fu = new Ftp();/** 使用默认的端口号、用户名、密码以及根目录连接FTP服务器*/fu.connectServer("127.0.0.1", 22, "anonymous", "IEUser@", "/temp");// 下载for (int i = 0; i < filepath.length; i++) {fu.download(filepath[i], localfilepath[i]);}String localfile = "E:\\号码.txt";String remotefile = "/temp/哈哈.txt";// 上传fu.upload(localfile, remotefile);fu.closeConnect();}
}// 这种方式没啥可说的,比较简单,也不存在中文乱码的问题。貌似有个缺陷,不能操作大文件,有兴趣的朋友可以试试。// 第二种方式
public class FtpApche {private static FTPClient ftpClient = new FTPClient();private static String encoding = System.getProperty("file.encoding");/*** Description: 向FTP服务器上传文件* * @Version1.0* @param url*            FTP服务器hostname* @param port*            FTP服务器端口* @param username*            FTP登录账号* @param password*            FTP登录密码* @param path*            FTP服务器保存目录,如果是根目录则为“/”* @param filename*            上传到FTP服务器上的文件名* @param input*            本地文件输入流* @return 成功返回true,否则返回false*/public static boolean uploadFile(String url, int port, String username,String password, String path, String filename, InputStream input) {boolean result = false;try {int reply;// 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器ftpClient.connect(url);// ftp.connect(url, port);// 连接FTP服务器// 登录ftpClient.login(username, password);ftpClient.setControlEncoding(encoding);// 检验是否连接成功reply = ftpClient.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {System.out.println("连接失败");ftpClient.disconnect();return result;}// 转移工作目录至指定目录下boolean change = ftpClient.changeWorkingDirectory(path);ftpClient.setFileType(FTP.BINARY_FILE_TYPE);if (change) {result = ftpClient.storeFile(new String(filename.getBytes(encoding), "iso-8859-1"),input);if (result) {System.out.println("上传成功!");}}input.close();ftpClient.logout();} catch (IOException e) {e.printStackTrace();} finally {if (ftpClient.isConnected()) {try {ftpClient.disconnect();} catch (IOException ioe) {}}}return result;}/*** 将本地文件上传到FTP服务器上* */public void testUpLoadFromDisk() {try {FileInputStream in = new FileInputStream(new File("E:/号码.txt"));boolean flag = uploadFile("127.0.0.1", 21, "zlb", "123", "/","哈哈.txt", in);System.out.println(flag);} catch (FileNotFoundException e) {e.printStackTrace();}}/*** Description: 从FTP服务器下载文件* * @Version1.0* @param url*            FTP服务器hostname* @param port*            FTP服务器端口* @param username*            FTP登录账号* @param password*            FTP登录密码* @param remotePath*            FTP服务器上的相对路径* @param fileName*            要下载的文件名* @param localPath*            下载后保存到本地的路径* @return*/public static boolean downFile(String url, int port, String username,String password, String remotePath, String fileName,String localPath) {boolean result = false;try {int reply;ftpClient.setControlEncoding(encoding);/** 为了上传和下载中文文件,有些地方建议使用以下两句代替 new* String(remotePath.getBytes(encoding),"iso-8859-1")转码。 经过测试,通不过。*/// FTPClientConfig conf = new// FTPClientConfig(FTPClientConfig.SYST_NT);// conf.setServerLanguageCode("zh");ftpClient.connect(url, port);// 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器ftpClient.login(username, password);// 登录// 设置文件传输类型为二进制ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 获取ftp登录应答代码reply = ftpClient.getReplyCode();// 验证是否登陆成功if (!FTPReply.isPositiveCompletion(reply)) {ftpClient.disconnect();System.err.println("FTP server refused connection.");return result;}// 转移到FTP服务器目录至指定的目录下ftpClient.changeWorkingDirectory(new String(remotePath.getBytes(encoding), "iso-8859-1"));// 获取文件列表FTPFile[] fs = ftpClient.listFiles();for (FTPFile ff : fs) {if (ff.getName().equals(fileName)) {File localFile = new File(localPath + "/" + ff.getName());OutputStream is = new FileOutputStream(localFile);ftpClient.retrieveFile(ff.getName(), is);is.close();}}ftpClient.logout();result = true;} catch (IOException e) {e.printStackTrace();} finally {if (ftpClient.isConnected()) {try {ftpClient.disconnect();} catch (IOException ioe) {}}}return result;}/*** 将FTP服务器上文件下载到本地* */public void testDownFile() {try {boolean flag = downFile("127.0.0.1", 21, "zlb", "123", "/","哈哈.txt", "D:/");System.out.println(flag);} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {FtpApche fa = new FtpApche();fa.testDownFile();}
}

使用Apache的FTP Client,实现:

package com.zhangjun.ftp;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;/*** ftp连接管理(使用apache commons-net-1.4.1 lib)* * @author 张军* @version 1.0 20120912*/
public class FTPClientUtil {private FTPClient ftpClient = null;private String server;private int port;private String userName;private String userPassword;public static void main(String[] args) {FtpClientUtil f = new FtpClientUtil("10.3.12.17", 21, "was", "was");try {if (f.open()) {// 远程路径为相对路径f.get("/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/qunarlog.txt", "E:/zhangjun.txt");f.close();}} catch (Exception e) {e.printStackTrace();}}public FtpClientUtil(String server, int port, String userName, String userPassword) {this.server = server;this.port = port;this.userName = userName;this.userPassword = userPassword;}/*** 链接到服务器* * @return* @throws Exception*/public boolean open() {if (ftpClient != null && ftpClient.isConnected()) {return true;}try {ftpClient = new FTPClient();// 连接ftpClient.connect(this.server, this.port);ftpClient.login(this.userName, this.userPassword);// 检测连接是否成功int reply = ftpClient.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {this.close();System.exit(1);}// 设置上传模式binally or asciiftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);return true;} catch (Exception ex) {// 关闭this.close();return false;}}private boolean cd(String dir) throws IOException {if (ftpClient.changeWorkingDirectory(dir)) {return true;} else {return false;}}/*** 获取目录下所有的文件名称* * @param filePath* @return* @throws IOException*/private FTPFile[] getFileList(String filePath) throws IOException {FTPFile[] list = ftpClient.listFiles();return list;}/*** 循环将设置工作目录*/public boolean changeDir(String ftpPath) {if (!ftpClient.isConnected()) {return false;}try {// 将路径中的斜杠统一char[] chars = ftpPath.toCharArray();StringBuffer sbStr = new StringBuffer(256);for (int i = 0; i < chars.length; i++) {if ('\\' == chars[i]) {sbStr.append('/');} else {sbStr.append(chars[i]);}}ftpPath = sbStr.toString();if (ftpPath.indexOf('/') == -1) {// 只有一层目录ftpClient.changeWorkingDirectory(new String(ftpPath.getBytes(), "iso-8859-1"));} else {// 多层目录循环创建String[] paths = ftpPath.split("/");for (int i = 0; i < paths.length; i++) {ftpClient.changeWorkingDirectory(new String(paths[i].getBytes(), "iso-8859-1"));}}return true;} catch (Exception e) {return false;}}/*** 循环创建目录,并且创建完目录后,设置工作目录为当前创建的目录下*/public boolean mkDir(String ftpPath) {if (!ftpClient.isConnected()) {return false;}try {// 将路径中的斜杠统一char[] chars = ftpPath.toCharArray();StringBuffer sbStr = new StringBuffer(256);for (int i = 0; i < chars.length; i++) {if ('\\' == chars[i]) {sbStr.append('/');} else {sbStr.append(chars[i]);}}ftpPath = sbStr.toString();if (ftpPath.indexOf('/') == -1) {// 只有一层目录ftpClient.makeDirectory(new String(ftpPath.getBytes(), "iso-8859-1"));ftpClient.changeWorkingDirectory(new String(ftpPath.getBytes(), "iso-8859-1"));} else {// 多层目录循环创建String[] paths = ftpPath.split("/");for (int i = 0; i < paths.length; i++) {ftpClient.makeDirectory(new String(paths[i].getBytes(), "iso-8859-1"));ftpClient.changeWorkingDirectory(new String(paths[i].getBytes(), "iso-8859-1"));}}return true;} catch (Exception e) {return false;}}/*** 上传文件到FTP服务器* * @param localPathAndFileName*            本地文件目录和文件名* @param ftpFileName*            上传后的文件名* @param ftpDirectory*            FTP目录如:/path1/pathb2/,如果目录不存在回自动创建目录* @throws Exception*/public boolean put(String localDirectoryAndFileName, String ftpFileName, String ftpDirectory) {if (!ftpClient.isConnected()) {return false;}boolean flag = false;if (ftpClient != null) {File srcFile = new File(localDirectoryAndFileName);FileInputStream fis = null;try {fis = new FileInputStream(srcFile);// 创建目录this.mkDir(ftpDirectory);ftpClient.setBufferSize(1024);ftpClient.setControlEncoding("UTF-8");// 设置文件类型(二进制)ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 上传flag = ftpClient.storeFile(new String(ftpFileName.getBytes(), "iso-8859-1"), fis);} catch (Exception e) {this.close();return false;} finally {IOUtils.closeQuietly(fis);s}}return flag;}/*** 从FTP服务器上下载文件并返回下载文件长度* * @param ftpDirectoryAndFileName* @param localDirectoryAndFileName* @return* @throws Exception*/public long get(String ftpDirectoryAndFileName, String localDirectoryAndFileName) {long result = 0;if (!ftpClient.isConnected()) {return 0;}ftpClient.enterLocalPassiveMode();try {// 将路径中的斜杠统一char[] chars = ftpDirectoryAndFileName.toCharArray();StringBuffer sbStr = new StringBuffer(256);for (int i = 0; i < chars.length; i++) {if ('\\' == chars[i]) {sbStr.append('/');} else {sbStr.append(chars[i]);}}ftpDirectoryAndFileName = sbStr.toString();String filePath = ftpDirectoryAndFileName.substring(0, ftpDirectoryAndFileName.lastIndexOf("/"));String fileName = ftpDirectoryAndFileName.substring(ftpDirectoryAndFileName.lastIndexOf("/") + 1);this.changeDir(filePath);ftpClient.retrieveFile(new String(fileName.getBytes(), "iso-8859-1"),new FileOutputStream(localDirectoryAndFileName)); // downloadSystem.out.print(ftpClient.getReplyString()); // check result} catch (IOException e) {e.printStackTrace();}return result;}/*** 返回FTP目录下的文件列表* * @param ftpDirectory* @return*/public List getFileNameList(String ftpDirectory) {List list = new ArrayList();if (!open())return list;try {DataInputStream dis = new DataInputStream(ftpClient.nameList(ftpDirectory));String filename = "";while ((filename = dis.readLine()) != null) {list.add(filename);}} catch (Exception e) {e.printStackTrace();}return list;return list;}/*** 删除FTP上的文件* * @param ftpDirAndFileName*/public boolean deleteFile(String ftpDirAndFileName) {if (!ftpClient.isConnected()) {return false;}return true;}/*** 删除FTP目录* * @param ftpDirectory*/public boolean deleteDirectory(String ftpDirectory) {if (!ftpClient.isConnected()) {return false;}return true;}/*** 关闭链接*/public void close() {try {if (ftpClient != null && ftpClient.isConnected())ftpClient.disconnect();} catch (Exception e) {e.printStackTrace();}}public FTPClient getFtpClient() {return ftpClient;}public void setFtpClient(FTPClient ftpClient) {this.ftpClient = ftpClient;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313708.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

L. Continuous Intervals(单调栈 + 线段树 + 思维)

L. Continuous Intervals 给定一个长度为nnn的数组&#xff0c;问里面有多少个区间[l,r][l, r][l,r]&#xff0c;满足&#xff0c;对这个区间排序后&#xff0c;两两差值$ \leq 1$&#xff0c;输出区间个数。 如果说区间[l,r][l, r][l,r]是符合要求的&#xff0c;则满足max(a…

Azure Application Insights REST API使用教程

本文是Azure Application Insights REST API的简单介绍&#xff0c;并会包含一个通过Python消费API的示例/小工具。新加入的team中的一项工作是制作日常的运维报表&#xff0c;制作方式是手工前往portal.azure.com&#xff0c;在网页中多次执行不同的查询语句、导出excel&#…

用Java Socket实现SMTP邮件发送

目录&#xff1a; 1、邮件基础概念2、Java Mail API介绍3、收发邮件代码示例 PS&#xff1a;如果你想直接拿代码用&#xff0c;可以直接跳到第3部分。 ** 一、邮件基础概念 ** 1.1 邮件服务器和电子邮箱 要在Internet上提供电子邮件功能&#xff0c;必须有专门的电子邮件…

微软开源新字体Cascadia Code,源于Windows Terminal

微软开源了一套新的字体 Cascadia Code。Cascadia Code 是微软在 5 月份的 Build 大会上宣布推出的等宽字体&#xff0c;微软介绍它是与新的终端 Windows Terminal 一起开发的&#xff0c;官方建议将其与终端应用和 VS、VS Code 等文本编辑器一起使用。Cascadia Code 为命令行和…

C. Safe Distance(二分 + 并查集)

C. Safe Distance&#xff08;二分 并查集&#xff09; 给定一个XYX \times YXY的矩形&#xff0c;里面有n,(1≤n≤1000)n,(1 \leq n \leq 1000)n,(1≤n≤1000)个点&#xff0c;我们要从点(0,0)(0, 0)(0,0)走到(X,Y)(X, Y)(X,Y)&#xff0c;我们要使过程中与这nnn个点的最小距…

进程间通讯的7种方式

1、常见的通信方式 管道pipe&#xff1a;管道是一种半双工的通信方式&#xff0c;数据只能单向流动&#xff0c;而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。命名管道FIFO&#xff1a;有名管道也是半双工的通信方式&#xff0c;但是它允许无亲缘…

官宣!VS Code Python 全新功能在 PyCon China 全球首发!

北京时间 2019 年 9 月 21 日&#xff0c;PyCon China 2019 在上海举行。在下午的演讲中&#xff0c;来自微软开发工具事业部的资深研发工程师在演讲中&#xff0c;我们看到了 Azure Notebook 与 VS Code 对 Python 的强大的支持。然而&#xff0c;鱼和熊掌似乎不可兼得。Jupyt…

什么是CDN

一、cdn简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。其目的是通过在现有的internet中增加一层新的网络架构&#xff0c;将网站的内容发布到最接近用户的网络边缘&#xff0c;使用户可以就近取得所需的内容&#xff0c;提高用户访问网站的响应速度。…

数列递推(牛客练习赛83)(数学、分块)

数列递推 给定f(0)f(0)f(0)&#xff0c;定义fn∑i1nf(nmodi)f_n \sum\limits_{i 1} ^{n} f_{(n \mod i)}fn​i1∑n​f(nmodi)​&#xff0c;求f1,f2,f3,…,fn−1,fnf_1, f_2, f_3, \dots, f_{n - 1}, f_{n}f1​,f2​,f3​,…,fn−1​,fn​。 ∑i1nf(nmodi)∑i1nf(n−nii)\su…

DNS基础

域名注册完成后首先需要做域名解析&#xff0c;域名解析就是把域名指向网站所在服务器的IP&#xff0c;让人们通过注册的域名可以访问到网站。IP地址是网络上标识服务器的数字地址&#xff0c;为了方便记忆&#xff0c;使用域名来代替IP地址。域名解析就是域名到IP地址的转换过…

基于Dapper的开源Lambda扩展,且支持分库分表自动生成实体之基础介绍

LnskyDB是基于Dapper的Lambda扩展,支持按时间分库分表,也可以自定义分库分表方法.而且可以T4生成实体类免去手写实体类的烦恼.文档地址: https://liningit.github.io/LnskyDB/开源地址: https://github.com/liningit/LnskyDBnuget地址: https://www.nuget.org/packages/LnskyDB…

CF1422F Boring Queries(ST表 + 主席树)

CF1422F Boring Queries 给定一个长度为nnn的数组a,(1≤ai≤2105)a,(1 \leq a_i \leq 2 \times 10 ^ 5)a,(1≤ai​≤2105)&#xff0c;有mmm次询问&#xff0c;每次询问给定l,rl, rl,r&#xff0c;要我们求区间[l,r][l, r][l,r]&#xff0c;aia_iai​的lcmlcmlcm&#xff0c;强…

高性能最终一致性框架Ray之基本概念原理

一、Actor介绍Actor是一种并发模型&#xff0c;是共享内存并发模型的替代方案。共享内存模型的缺点&#xff1a;共享内存模型使用各种各样的锁来解决状态竞争问题&#xff0c;性能低下且让编码变得复杂和容易出错。共享内存受限于单节点的服务器资源限制。Actor模型的优点&…

B.The Tortoise and the Hare 长春

B. The Tortoise and the Hare 给定一个长度为nnn的数组a,(1≤ai<m)a, (1 \leq a_i < m)a,(1≤ai​<m)&#xff0c;mmm是一个给定的数(1≤m≤109)(1 \leq m \leq 10 ^ 9)(1≤m≤109)&#xff0c;有QQQ次操作&#xff0c;分为两类&#xff1a; 给定u,v,(1≤u≤n,1≤v…

漫画:程序员带娃日常(1)

我是一名IT界资深人士做过乙方苦哈哈做过甲方做过项目、搞点管理做过培训、也上台演讲不过现在最大挑战、交期最长的项目是。。。带 娃娃子啊&#xff0c;爸爸也是第一次当爸爸&#xff0c;要请你多多指教啦&#xff01;1“小刘&#xff0c;把这个需求细化一下。”“兄弟们&…

L. Coordinate Paper(CCPC 长春)构造

L. Coordinate Paper 构造一个长度为nnn的序列aaa&#xff0c;满足ai≥0a_i \geq 0ai​≥0&#xff0c;∑i1nais\sum\limits_{i 1} ^{n} a_i si1∑n​ai​s&#xff0c;对于任意的i∈[1,n−1]i \in [1, n - 1]i∈[1,n−1]&#xff0c;都有ai−ai1korai1−ai1a_i - a_{i 1} …

servlet生命周期

1.servlet的生命周期 主要有三个方法&#xff1a; init()初始化阶段service()处理客户端请求阶段destroy()终止阶段 初始化阶段&#xff1a; Servlet容器加载Servlet&#xff0c;加载完成后&#xff0c;Servlet容器会创建一个Servlet实例并调用init()方法&#xff0c;init(…

表达式树练习实践:入门基础

什么是表达式树来自微软官方文档的定义&#xff1a;表达式树以树形数据结构表示代码。它能干什么呢&#xff1f;你可以对表达式树中的代码进行编辑和运算。这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。好不好玩&#xff1f;表达式树还能用于动…

Servlet与线程安全

Servlet与线程安全 先说结论&#xff0c;Servlet本身是单例的&#xff0c;线程安全的。但是如果引入共享变量&#xff0c;则可能会变得线程不安全。 1. 什么是线程安全 首先说明一下对线程安全的讨论&#xff0c;哪种情况我们可以称作线程安全&#xff1f; 《Java并发编程实…

P3591 [POI2015]ODW(分块)

P3591 [POI2015]ODW 给定一颗有nnn个节点的树&#xff0c;点有点权&#xff0c;给定一个长度为nnn的排列ppp&#xff0c;给定一个长度为n−1n - 1n−1的数组ccc&#xff0c; 我们会在树上进行n−1n - 1n−1次行走&#xff0c;第iii次我们会从p[i]p[i]p[i]走到p[i1]p[i 1]p[i…