重温java 系列一 Java基础
文件拷贝的5种方式
传统字节拷贝
public static void main(String[] args) throws IOExecption{
try(InputStream is = new FileInputStream("source.txt");
OutputStream os = new FileOutputStream("target.txt")){
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer))>0){
os.write(buffer,0,length);
}
}
}
- 特点:基础方法,直接逐字节或缓冲区读写。
- 效率: 最低 (适合小文件)
缓冲流优化拷贝
public static void main(String[] args) throws FileNotFoundException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("target.txt"))
){
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- 特点: 通过换红减少IO次数
- **效率:**比传统字节流提升2~5倍
Java NIO 拷贝
public static void main(String[] args) throws IOException {
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
Files.copy(source,target, StandardCopyOption.REPLACE_EXISTING);
}
- 特点: 利用通道(Channel)直接传输数据
- **效率:**大文件性能最佳(利用了零拷贝技术)
内存映射文件拷贝
public static void main(String[] args) throws IOException {
try (RandomAccessFile source = new RandomAccessFile("source.txt", "r");
RandomAccessFile target = new RandomAccessFile("target.txt", "rw")) {
FileChannel sourceChannel = source.getChannel();
MappedByteBuffer buffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, sourceChannel.size());
target.getChannel().write(buffer);
}
- **特点:**将文件映射到内存中直接操作
- **效率:**适合超大文件(实现复杂,需要谨慎处理内存)
中止线程的三种方式
停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作,也就是放弃当前的操作。
在Java中有以下3种方式可以种植正在运行的线程:
- 使用推出标志,使线程正常退出,也就是当run()方法完成后线程中止
- 使用stop()方法强行终止线程,但是不推荐使用此方法,该方法已被弃用
- 使用interrupt 方法中断主线程
1. 使用标志位终止线程(推荐)
在run()方法执行完毕后,该线程就终止了,但是在某些特殊情况下,run()方法中会被一直执行,比如在服务端程序中可能会使用while(true){…}这样的循环结构来不断的接受来自客户端的请求,此时就可以用修改标志位的方式来结束run 方法。
public class Main {
private volatile boolean exitFlag = false;
public void run(){
while (!exitFlag){
System.out.println("Thread is running.....");
try {
Thread.sleep(1000);
}catch (InterruptedException e){
System.out.println("Thread interrupted");
Thread.currentThread().interrupt();
}
}
System.out.println("Thread exiting.....");
}
public void stop(){
exitFlag = true;
}
public static void main(String[] args) throws IOException, InterruptedException {
Main main = new Main();
Thread thread = new Thread(main::run);
thread.start();
Thread.sleep(1000);
main.stop();
}
}
2. 使用stop()中止线程(不推荐)
Thread.stop()方法可以强制中止线程的执行,然而,这种方法是不安全的,因为他不能保证线程资源的正确释放和清理,可能导致数据不一致和资源泄露等问题,因此该方法已被官方弃用。
public class Main extends Thread{
public void run(){
while (true){
System.out.println("Thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.start();
Thread.sleep(5000);
main.stop();
}
}
通过JDK的Api,我们会看到java.lang.Thread 类型提供了一系列的方法,如start(),stop(),resume(),suspend(),destory()等方法来管理线程。但是除了start()之外,其他几个方法都被声明为已过时(@Deprecated)。
虽然stop()方法确实可以停止一个正在运行的线程,但是这个方法 是不安全的,而且该方法已被弃用,最好不要使用它。
为什么弃用stop():
- 调用stop()方法会立刻停止run()方法中剩余的全部工作,包括在catch或者finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作得不到完成,如文件、数据库等等的关闭操作。
- 调用stop()方法会立即释放该线程所持有的锁,导致数据得不到同步,导致数据不一致的问题。
使用interrupt()中断线程
现在我们知道了使用stop()方式停止线程是非常不安全的方式,那么我们应该使用什么方法来停止线程呢?答案就是使用interrupt()方法来中断线程。
需要明确的一点是:interrupt()方法并不像在for循环语句中使用break语句那样干脆,马上就停止循环。调用interrupt()方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。
也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止,至于目标线程收到通知后会如何处理,则完全由目标线程自行据欸的那个,这一点很重要。如果中断后,线程立即无条件推出,那么我们又会遇到stop()方法的老问题。
事实上,如果一个线程不能被interrupt,那么stop方法也不会起作用。
接下来我们看一个使用了interrupt()的例子:
public class Main extends Thread{
// public static void main(String[] args)
// {
// SpringApplication.run(Main.class, args);
// }
@Override
public void run() {
super.run();
for (int i = 0; i <10000; i++) {
System.out.println("i="+i);
}
}
public static void main(String[] args) {
try {
Main t = new Main();
t.start();
Thread.sleep(1000);
t.interrupt();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
结果如下:
i=9986
i=9987
i=9988
i=9989
i=9990
i=9991
i=9992
i=9993
i=9994
i=9995
i=9996
i=9997
i=9998
i=9999
从输出的结果我们会发现interrupt方法并没有停止线程t中的处理逻辑,也就是说即使t线程被设置成了中断状态,但是这个中断并不会起作用,那么该如何停止该线程呢?
这就需要使用到另外两个与线程中断有关的方法了
public boolean Thread.isInterrupted() //判断是否被中断
public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态
这两个方法使得当前线程能够感知到是否被中断了(通过检查标志位)
所以如果希望线程t 在中断后停止,就必须先判断是否被中断,并为它增加相应的中断处理代码:
@Override
public void run() {
super.run();
for (int i = 0; i <10000; i++) {
if (Thread.currentThread().isInterrupted()){
//处理中断逻辑
break;
}
System.out.println("i="+i);
}
}
在上面这段代码中,我们增加了Thread.isInterrupted()来判断当前线程是否被中断了,如果是,则退出for 循环,结束线程。
这种方式看起来与之前介绍的“使用标志位中止线程”非常类似,但是遇到了sleep()或者wait()这样的操作,我们只能通过中断来处理。
public static native sleep(long millis) throws InterrupedExecption
Thread.Sleep()方法会抛出一个InterruptedException 异常,当线程被sleep()休眠时 ,如果被中断,就会抛出这个异常。
注意:Thread.sleep() 方法由于中断额抛出 的异常,是会清除中断标记的