从零开始学Java,学习笔记Day23

news2024/12/19 2:06:41

Day23

一、线程池

引入

一个线程完成一项任务所需时间为:

  1. 创建线程时间 - Time1
  2. 线程中执行任务的时间 - Time2
  3. 销毁线程的时间 - Time3

为什么需要线程池

  1. 线程池技术正式关注如何缩短或调整Time1和Time3的时间,从而提高程序性能。项目中可以啊Time1,Time3分别安排在项目启动和结束的时间段或者一些空闲的时间断
  2. 线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建线程的数目,提高线程的复用率
  3. 系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情况下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑线程池

Java提供的线程池

ExecutorService:线程池接口

Executors:创建各种线程池的工具类

public class Test {
    public static void main (String[] args) {
        //创建单个线程的线程池
        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建指定线程的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //创建可缓存线程的线程池,自动回收60s闲置的线程
        ExecutorService pool = Executors.newCachedThreadPool();
        
        for (int i = 1; i <= 100; i++) {
            pool.execute(new Task(i));//提交任务,i为任务编号(方便理解)
        }
        pool.shutdown();//关闭线程池
        
    }
}
public class Task implements Runnable {
    private int i;
    public Task(int i) {
        this.i = i;
    }
    
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " --> " + i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedExcepion e) {
            e.printStackTrace();
        }
    }
}

深入源码

ExecutorService pool = Executors.newSingleThreadExecutor();

ExecutorService pool = Executors.newFixedThreadPool(3);

ExecutorService pool = Executors.newCachedThreadPool();

三种线程池底层都是ThreadPoolExecutor类的对象

-- 分析ThreadPoolExecutor类的构造方法源码--------------------------------
public ThreadPoolExecutor(
	int corePoolSize,		------------- 核心线程数量 
    int maximumPoolSize,	------------- 最大线程数量 
	long keepAliveTime,	   ------------- 闲置时间,作用于核心线程数与最大线程数之间的线程
	TimeUnit unit,			------------- keepAliveTime的时间单位(可以是毫秒、秒....)
	BlockingQueue<Runnable> workQueue, -- 任务队列
	ThreadFactory threadFactory, -------- 线程工厂
	RejectedExecutionHandler handler ---- 达到了线程界限和队列容量时的处理方案(拒绝策略)
) {}
执行步骤:
	1.创建线程池后
    2.任务提交后,查看是否有核心线程:
        3.1 没有 -> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中
        3.2-> 查看是否有闲置核心线程:
        	4.1-> 执行任务 -> 执行完毕后又回到线程池
        	4.2 没有 -> 查看当前核心线程数是否核心线程数量:
        		5.1-> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中
        		5.2-> 查看任务列表是否装载满:
        			6.1 没有 -> 就放入列表中,等待出现闲置线程
        			6.2 装满 -> 查看是否有普通线程(核心线程数到最大线程数量之间的线程)
        				7.1 没有 -> 就创建普通线程 -> 执行任务 -> 执行完毕后又回到线程池中
        				7.2-> 查看是否有闲置普通线程
        					7.1.1-> 执行任务 -> 执行完毕后又回到线程池中
        					7.1.2 没有 -> 查看现在所有线程数量是否为最大线程数:
        						8.1-> 执行处理方案(默认处理抛出异常)
        						8.2->就创建普通线程-> 执行任务 -> 执行完毕后又回到线程池中     			
注:
	1.为了更好的理解,在这里区分核心线程和普通线程,实际上区分的没这么清楚,都是线程
    2.默认的处理方案就是抛出RejectedExecutionException
总结:核心线程 -> 任务队列 -> 普通线程 -> 拒绝策略

-- 分析单个线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newSingleThreadExecutor();
new ThreadPoolExecutor(
    1, -- 核心线程数量 
    1, -- 最大线程数量 
    0L, -- 闲置时间
    TimeUnit.MILLISECONDS, -- 时间单位(毫秒)
    new LinkedBlockingQueue<Runnable>() -- 无界任务队列,可以无限添加任务
)  
-- 分析指定线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newFixedThreadPool(3);
new ThreadPoolExecutor(
    nThreads, -- 核心线程数量 
    nThreads, -- 最大线程数量 
    0L, -- 闲置时间
    TimeUnit.MILLISECONDS, -- 时间单位(毫秒)
    new LinkedBlockingQueue<Runnable>()-- 无界任务队列,可以无限添加任务
)
-- 创建可缓存线程的线程池 -----------------------------------
ExecutorService pool = Executors.newCachedThreadPool();
new ThreadPoolExecutor(
    0, -- 核心线程数量 
    Integer.MAX_VALUE,-- 最大线程数量 
    60L, -- 闲置时间
    TimeUnit.SECONDS, -- 时间单位()
    new SynchronousQueue<Runnable>() -- 直接提交队列(同步队列):没有容量队列

任务队列详解

队列名称详解
LinkedBlockingQueue无界任务队列使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题
SynchronousQueue 同步任务队列 直接提交任务队列使用直接提交任务队列,队列没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。任务队列为SynchronousQueue,创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。 使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的线程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;
ArrayBlockingQueue有界任务队列使用有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。
PriorityBlockingQueue优先任务队列使用优先任务队列,它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

对优先队列的使用说明:

public class Test {
	
	public static void main(String[] args) {
		
		ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 200, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
		
		for (int i = 1; i <= 10; i++) {
			pool.execute(new Task(i));
		}
		
		pool.shutdown();
		
	}
}
class Task implements Runnable,Comparable<Task>{
	
	private int priority;
	
	public Task(int priority) {
		this.priority = priority;
	}

	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + " priority:" + this.priority);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	//当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高
	@Override
	public int compareTo(Task o) {
		return (this.priority>o.priority)?-1:1;
	}
}
总结:除了第一个任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,也就是只有一个。

拒绝策略

ThreadPoolExecutor自带的拒绝策略有四种,都实现了RejectedExecutionHandler接口

比如:new ThreadPoolExecutor.AbortPolicy()

拒绝策略解释
AbortPolicy当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException异常,线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
DiscardPolicy当有任务添加到线程池被拒绝时,直接丢弃,其他啥都没有
CallerRunsPolicy当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到线程池正在运行的线程中去运行。 一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
DiscardOledestPolicy当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务(最老的任务–第一个添加的任务),然后将被拒绝的任务添加到末尾。 如果项目中有允许丢失任务的需求,可以使用

自定义拒绝策略

public class Test {
	
	public static void main(String[] args) {
		
		ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
				System.out.println(r.toString()+"执行了拒绝策略");
			}
		});
		
		for (int i = 1; i <= 10; i++) {
			pool.execute(new Task());
		}
		
		pool.shutdown();
	}
}
class Task implements Runnable{
	
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

自定义线程池原因

在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面使线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活,而且有资源耗尽的风险(OOM - Out Of Memory )。

一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但这种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况

自定义线程池

前提学习线程池中如何创建线程:

线程池中线程就是通过ThreadPoolExecutor中的ThreadFactory,线程工厂创建的。那么通过自定义ThreadFactory,可以按需要对线程池中创建的线程进行一些特殊的设置,如命名、优先级等

public class Test {

	public static void main(String[] args) {

		ThreadPoolExecutor pool = new ThreadPoolExecutor(
				10,20, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), 
				new ThreadFactory() {
					int threadNum = 1;
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r,"线程"+threadNum++);
						return t;
					}
				}, new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println(r.toString()+"执行了拒绝策略");
					}
			});

		for (int i = 1; i <= 10; i++) {
			pool.execute(new Task());
		}

		pool.shutdown();

	}
}
class Task implements Runnable{
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

ThreadPoolExecutor扩展

ThreadPoolExecutor扩展主要是围绕beforeExecute()、afterExecute()和terminated()三个方法的重写, 通过这三个方法我们可以监控每个任务的开始和结束时间,或者其他一些功能。

方法解释
beforeExecute线程池中任务运行前执行
afterExecute线程池中任务运行完毕后执行
terminated线程池退出后执行

下面我们可以通过代码实现一下

ThreadPoolExecutor pool = new ThreadPoolExecutor(
				10, 10, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10), 
				new ThreadFactory() {
					int threadNum = 1;
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r,"线程"+threadNum++);
						return t;
					}
				}, new ThreadPoolExecutor.AbortPolicy()
			){
				@Override
				protected void beforeExecute(Thread t,Runnable r) {
	                System.out.println("准备执行:"+ Thread.currentThread().getName());
	            }
				@Override
	            protected void afterExecute(Runnable r,Throwable t) {
	                System.out.println("执行完毕:"+ Thread.currentThread().getName());
	            }
				@Override
	            protected void terminated() {
	                System.out.println("线程池退出");
	            }
		};

		for (int i = 1; i <= 10; i++) {
			pool.execute(new Task());
		}

		pool.shutdown();

	}
}
class Task implements Runnable{
	
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println("执行中:" + Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

线程池线程数量

实际工作中使用 sysbench多线程性能测试工具

带返回值的任务类 – Callable

计算任务:一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果

​ 注意:使用线程池 + 带有返回值的任务类去解决该需求

public class Test {
    public static void main(String[] args) {
        //常见数据源
        int[] arr = new int[20000];
        //初始化数据源 - [1,2,3,4,...,20000]
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i+1;
        }
        
        //创建线程池
        FastThreadPool pool = new FastThreadPool(4,4,1);
        //提交任务,并返回任务执行完毕时的结果对象
        Future<Integer> future1 = pool.submit(new Task(arr, 0, 5000));
        Future<Integer> future2 = pool.submit(new Task(arr, 5000, 10000));	
		Future<Integer> future3 = pool.submit(new Task(arr, 10000, 15000));	
		Future<Integer> future4 = pool.submit(new Task(arr, 15000, 20000));
        
        //获取任务执行结果,并合并
        int result = futurel.get() + future2.get() + future3.get() + future4.get();
        System.out.println(result);
        pool.shutdown();
    }
}

class Task implements Callable<Integer> {
    
    private int[] arr;
    private int startIndex;
    private int endIndex;
    
    public Task(int[] arr, int startIndex, Int enIndex) {
        this.arr = arr;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }
    
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = startIndex; i < endIndex; i++) {
            sum += arr[i];
        }
        return sum;
    }
    
}

二、File – 文件类

理解:File类的对象可以描述一个目录或者文件

注意:

  1. File类的对象可以获取文本信息,但是不能操作文件的内容
  2. 要想操作文件的内容需要通过IO流技术

需求:使用File类的常用方法获取文件的信息

public class Test {
    public static void main(String[] args) {
        File file = new File("D:\\蔡徐坤\\合计.txt");
        System.out.println("获取文件名:" + file.getName());
		System.out.println("获取文件绝对路径:" + file.getAbsolutePath());
		System.out.println("获取文件是否可读:" + file.canRead());
		System.out.println("获取文件是否可写:" + file.canWrite());
		System.out.println("获取文件是否隐藏:" + file.isHidden());
		System.out.println("获取文件大小(字节):" + file.length());
        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		String datetime = sdf.format(file.lastModified());
		System.out.println("获取文件最后的修改时间:" + datetime);
    }
}

绝对路径 和 相对路径

绝对路径:指定盘符的路径

相对路径:相对于当前项目下的路径

public class Test {
    	public static void main(String[] args) {
		
		File file = new File("file.txt");
		
		//D:\workspace\Day23\file.txt
		System.out.println("绝对路径:" + file.getAbsolutePath());
		
		//file.txt
		System.out.println("相对路径:" + file.getPath());
		
	}
}

需求:通过程序,判断指定路径的文件是否存在,如果不存在,则创建该文件

  1. 目录已存在的情况

    public static void main(String[] args) throws IOException {
    
        File file = new File("file01\\file.txt");
    
        //判断是否有当前文件
        if(!file.exists()){
            //创建文件
            file.createNewFile();
        }
    
    }
    
  2. 有一个层级的目录不存在的情况

    public static void main(String[] args) throws IOException {
    
        File file = new File("file01\\file.txt");
    
        //获取上一级file对象 -- file01
        File parentFile = file.getParentFile();
        if(!parentFile.exists()){
            //创建一层文件夹
            parentFile.mkdir();
        }
    
        //判断是否有当前文件
        if(!file.exists()){
            //创建文件
            file.createNewFile();
        }
    
    }
    
  3. 有多个层级的目录不存在的情况

    public static void main(String[] args) throws IOException {
    
        File file = new File("file01\\file02\\file03\\file.txt");
    
        //获取上一级file对象 -- file01\\file02\\file0
        File parentFile = file.getParentFile();
        if(!parentFile.exists()){
            //创建多层文件夹
            parentFile.mkdirs();
        }
    
        //判断是否有当前文件
        if(!file.exists()){
            //创建文件
            file.createNewFile();
        }
    
    }
    

需求:输出指定目录下的所有文件信息

public static void main(String[] args) {

    File file = new File("D:\\飞秋共享");

    //获取当前目录下所有的文件名
    //		String[] list = file.list();
    //		for (String str : list) {
    //			System.out.println(str);
    //		}

    //获取当前目录下所有的文件对象
    File[] listFiles = file.listFiles();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
    for (File f : listFiles) {

        String name = f.getName();
        String datetime = sdf.format(f.lastModified());
        System.out.println(name + " -- " + datetime);
    }
}
  1. 要求只输出文件后缀名为xmind的文件

    public static void main(String[] args) {
    
        File file = new File("D:\\蔡徐坤");
    
        //获取当前目录下所有的文件名
        //		String[] list = file.list();
        //		for (String str : list) {
        //			if(str.endsWith(".xmind")){
        //				System.out.println(str);
        //			}
        //		}
    
        //获取当前目录下所有的文件对象
        File[] listFiles = file.listFiles();
        for (File f : listFiles) {
            String name = f.getName();
            if(f.isFile() && name.endsWith(".xmind")){
                System.out.println(name);
            }
        }
    }
    
  2. 根据API的过滤器来完成该功能

    public static void main(String[] args) {
    
        File file = new File("D:\\蔡徐坤");
    
        File[] listFiles = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
    
                File f = new File(dir, name);
                if(f.isFile() && name.endsWith(".xmind")){
                    return true;
                }
                return false;
            }
        });
    
        for (File f : listFiles) {
            System.out.println(f);
        }
    }
    
  3. 需求继续跟进,列出当前目录及子目录中符合该条件的文件信息(递归)

    public static void main(String[] args) {
    
        File file = new File("D:\\蔡徐坤");
        method(file, ".txt");
    }
    
    public static void method(File file,String suffix){
        File[] files = file.listFiles();
        for (File f : files) {
            if(f.isDirectory()){
                method(f, suffix);
            }else if(f.isFile()){
                if(f.getName().endsWith(suffix)){
                    System.out.println(f.getAbsolutePath());
                }
            }
        }
    }
    

三、IO流

概念

I - in - 输入流:将数据传入到程序内容 -> read()

O - out - 输出流:将程序里的数据传出去 -> writer()

流:一点点,像水流一般的传输

注意:输入、输出的方向是站在程序的角度考虑

流的分类

按照方向分:输入流、输出流

按照单位分:字节流、字符流

按照功能分:基础流/节点流、处理流

注意:

  1. 字符流底层使用字节流实现,字符流其实就是字节流+编码器
  2. 处理流的功能更加强大,效率更高
  3. 处理流往往包含了基础流(装饰器模式) – new 处理流(new 基础流());

存储进制

1024KB = 1MB

1024MB = 1GB

1024GB = 1TB

1024TB = 1PB

字节流

InputStream – 字节输入流的基类(抽象类)

OutputStream – 字节输出流的基类(抽象类)

FileInputStream extends InputStream – 文件字节输入流

FileOutputStream extends OutputStream – 文件字节输出流

利用字节输出流 向 文件写入数据

  • 文件存在的情况

  • 不处理异常的情况

    public static void main(String[] args) throws IOException {
    
        //1.创建流对象
        FileOutputStream fos = new FileOutputStream("cxk.txt");
    
        //2.写入数据
        //fos.write(97);//写入字符码值
        //fos.write("123abc".getBytes());//写入字节数组
        fos.write("123abc".getBytes(), 3, 3);//写入字节数组,偏移量,写入长度
    
        //3.关闭资源
        fos.close();
    }
    
  • 文件不存在的情况

    • 经验:
      • 所有的输出流,当文件不存在时,都会创建文件,再写入
      • 所有的输入流,当文件不存在时,就会报错 – FileNotFoundException文件未找到异常
  • 在文件末尾追加

    经验:考虑输出流的基础流

  • 处理异常的情况

    public static void main(String[] args) {
    
        FileOutputStream fos = null;
        try {
            //1.创建流对象
            fos = new FileOutputStream("cxk.txt", true);
            //2.写入数据
            fos.write("123abc".getBytes());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭资源
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

利用字节输入流 读取文件里的数据

  1. 不处理异常的方式

  2. 文件存在的情况

  3. 文件不存在的情况

    • 经验:
      • 所有的输出流,当文件不存在时,都会创建文件,再写入
      • 所有的输入流,当文件不存在时,就会报错 – FileNotFoundException文件未找到异常
    public static void main(String[] args) throws IOException {
    
        //1.创建流对象
        FileInputStream fis = new FileInputStream("cxk.txt");
    
        //2.读取数据
        //read():一个一个的读取字符,读取到文件末尾返回-1
        int read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println(read);
    
        //3.关闭资源
        fis.close();
    
    }
    
    public static void main(String[] args) throws IOException {
    
        //1.创建流对象
        FileInputStream fis = new FileInputStream("kk.txt");
    
        //2.读取数据:一个一个字节的循环读取
        int read;
        while((read = fis.read()) != -1){
            System.out.println((char)read);
        }
    
        //3.关闭资源
        fis.close();
    }
    
    public static void main(String[] args) throws IOException {
    
        //1.创建流对象
        FileInputStream fis = new FileInputStream("cxk.txt");
    
        //2.读取数据:一个一个字节数组的循环读取
        //fis.read(bs):读取bs长度的数据放入到数组中,返回读取到的有效字节数;读取到文件末尾返回-1
        byte[] bs = new byte[1024];
        int len;
        while((len = fis.read(bs)) != -1){
            System.out.println(new String(bs, 0, len));
        }
    
        //3.关闭资源
        fis.close();
    
    }
    
    1. 处理异常的方式
    public static void main(String[] args) {
    
    
        FileInputStream fis = null;
        try {
            //1.创建流对象
            fis = new FileInputStream("cxk.txt");
            //2.读取数据:一个一个字节数组的循环读取
            //fis.read(bs):读取bs长度的数据放入到数组中,返回读取到的有效字节数;读取到文件末尾返回-1
            byte[] bs = new byte[1024];
            int len;
            while((len = fis.read(bs)) != -1){
                System.out.println(new String(bs, 0, len));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭资源
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

拷贝文件

思路:读取源文件,写入目标文件

public class Copy01 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("源文件.mp4");
            fos = new FileOutputStream("目标文件.mp4");
            
            //一个一个字节去拷贝
            int read;
            while ((read = fis.read() != -1)) {
                fos.write(read);
            }
        } catch (IOException e) {
            e.printstackTrace();
        } finally {
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
    }
}
public class Copy02 {

	public static void main(String[] args) {
		
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream("源文件.mp4");
			fos = new FileOutputStream("目标文件.mp4");
			
			//一个一个字节数组去拷贝
			byte[] bs = new byte[1024];
			int len;
			while((len = fis.read(bs)) != -1){
				fos.write(bs, 0, len);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
}

public class Copy03 {

	public static void main(String[] args) {
		
		
		try(
			//在这个位置创建的流,会在try执行完毕后自行关闭
			FileInputStream fis = new FileInputStream("源文件.mp4");
			FileOutputStream fos = new FileOutputStream("目标文件.mp4");
				) {
			//一个一个字节数组去拷贝
			byte[] bs = new byte[1024];
			int len;
			while((len = fis.read(bs)) != -1){
				fos.write(bs, 0, len);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

总结:

  1. 线程池
    理解
    Java自带的线程池
    线程池的7大参数及调用过程
    为什么不适用java自带的线程池的原因
    自定义线程池(自定义线程工厂、自定义拒绝策略)
  2. File类
  3. IO流 – 字节流

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

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

相关文章

GB28181系列三:GB28181流媒体服务器ZLMediaKit

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、ZLMediaKit介绍 二、 ZLMediaKit安装、运行(Ubuntu) 1、安装 2、运行 3、配置 三、ZLMediaKit使用 一、ZLMediaKit介绍 ZLMediaKit是一个基于C11的高性能运营级流媒体服务框架&#xff0c;项目地址&#xf…

【深度学习】深刻理解Swin Transformer

Swin Transformer 是一种基于 Transformer 的视觉模型&#xff0c;由 Microsoft 研究团队提出&#xff0c;旨在解决传统 Transformer 模型在计算机视觉任务中的高计算复杂度问题。其全称是 Shifted Window Transformer&#xff0c;通过引入分层架构和滑动窗口机制&#xff0c;S…

uniCloud云开发视频教程-从基础入门到项目开发实战-uniapp进阶课文章管理系统(云函数/云数据库/云存储)

大家好&#xff0c;我是爱搞知识的咸虾米。 今天给大家带来一门uniCloud基础入门到项目开发实战的课程。 视频学习地址&#xff1a;https://www.bilibili.com/video/BV1PP411E7qG/ 开始学习这门课之前&#xff0c;最好先学习一下uniapp零基础入门这套课&#xff0c;相信很多同…

GLB格式转换为STL格式

GLB与STL格式简介 GLB格式 GLB代表“GL传输格式二进制文件”&#xff08;GL Transmission Format Binary&#xff09;。GLB主要用于共享3D数据&#xff0c;包含三维模型、场景、光源、材质、节点层次和动画等详细信息&#xff0c;是一种标准化的文件格式&#xff0c;适用于多…

Qt编译MySQL数据库驱动

目录 Qt编译MySQL数据库驱动 测试程序 Qt编译MySQL数据库驱动 &#xff08;1&#xff09;先找到MySQL安装路径以及Qt安装路径 C:\Program Files\MySQL\MySQL Server 8.0 D:\qt\5.12.12 &#xff08;2&#xff09;在D:\qt\5.12.12\Src\qtbase\src\plugins\sqldrivers\mysql下…

MySQL通过binlog日志进行数据恢复

记录一次阿里云MySQL通过binlog日志进行数据回滚 问题描述由于阿里云远程mysql没有做安全策略 所以服务器被别人远程攻击把数据库给删除&#xff0c;通过查看binlog日志可以看到进行了drop操作&#xff0c;下面将演示通过binlog日志进行数据回滚操作。 1、查询是否开始binlog …

如何在 Ubuntu 22.04 上安装和使用 Rust 编程语言环境

简介 Rust 是一门由 Mozilla 开发的系统编程语言&#xff0c;专注于性能、可靠性和内存安全。它在没有垃圾收集的情况下实现了内存安全&#xff0c;这使其成为构建对性能要求苛刻的应用程序&#xff08;如操作系统、游戏引擎和嵌入式系统&#xff09;的理想选择。 接下来&…

前端项目初始化搭建(二)

一、使用 Vite 创建 Vue 3 TypeScript 项目 PS E:\web\cursor-project\web> npm create vitelatest yf-blog -- --template vue-ts> npx > create-vite yf-blog --template vue-tsScaffolding project in E:\web\cursor-project\web\yf-blog...Done. Now run:cd yf-…

生活小妙招之UE CaptureRT改

需求&#xff0c;四个不同的相机拍摄结果同屏分屏显示 一般的想法是四个Capture拍四张RT&#xff0c;然后最后在面片/UI上组合。这样的开销是创建4张RT&#xff0c;材质中采样4次RT。 以更省的角度&#xff0c;想要对以上流程做优化&#xff0c;4个相机拍摄是必须的&#xff…

【AIGC进阶-ChatGPT提示词副业解析】探索生活的小确幸:在平凡中寻找幸福

引言 在这个快节奏的现代社会中,我们常常被各种压力和焦虑所困扰,忘记了生活中那些细小而珍贵的幸福时刻。本文将探讨如何在日常生活中发现和珍惜那些"小确幸",以及如何通过尝试新事物来丰富我们的生活体验。我们还将讨论保持神秘感和期待感对于维持生活乐趣的重要性…

C#编程报错- “ComboBox”是“...ComboBox”和“...ComboBox”之间的不明确的引用

1、问题描述 在学习使用C#中的Winform平台编写一个串口助手程序时&#xff0c; 在编写一个更新ComboBox列表是遇到了问题&#xff0c;出错的代码是 2、报错信息 CS1503 参数 2: 无法从“System.Windows.Forms.ComboBox”转换为“System.Windows.Forms.ComboBox” CS1503 …

ollama+open-webui,本地部署自己的大模型

目录 一、效果预览 二、部署ollama 1.ollama说明 2.安装流程 2.1 windows系统 2.1.1下载安装包 2.1.2验证安装结果 2.1.3设置模型文件保存地址 2.1.4拉取大模型镜像 2.2linux系统 2.2.1下载并安装ollama 2.2.2设置环境变量 2.2.3拉取模型文件 三、部署open-webui…

leetcode_203. 移除链表元素

203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 开始写的时候没有想明白的问题 1. 开始我是想头节点 尾节点 中间节点 分开处理 如果删除的是头节点 然后又要删除头节点的后继节点 那么 这样子的话头节点分开处理就毫无意义了 接着是尾节点 开始我定义的是curr h…

【大模型微调学习5】-大模型微调技术LoRA

【大模型微调学习5】-大模型微调技术LoRA LoRa微调1.现有 PEFT 方法的局限与挑战2.LoRA: 小模型有大智慧 (2021)3.AdaLoRA: 自适应权重矩阵的高效微调 (2023)4.QLoRA: 高效微调量化大模型 (2023) LoRa微调 1.现有 PEFT 方法的局限与挑战 Adapter方法&#xff0c;通过增加模型深…

.NET 技术系列 | 通过CreatePipe函数创建管道

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

DS18B20温度传感器(STM32)

一、介绍 DS18B20是一种常见的数字型温度传感器&#xff0c;具备独特的单总线接口方式。其控制命令和数据都是以数字信号的方式输入输出&#xff0c;相比较于模拟温度传感器&#xff0c;具有功能强大、硬件简单、易扩展、抗干扰性强等特点。 传感器参数 测温范围为-55℃到1…

shell编程2 永久环境变量和字符串显位

声明 学习视频来自B站UP主 泷羽sec 常见变量 echo $HOME &#xff08;家目录 root用户&#xff09; /root cd /root windows的环境变量可以去设置里去新建 为什么输入ls dir的命令的时候就会输出相应的内容呢 因为这些命令都有相应的变量 which ls 通过这个命令查看ls命令脚本…

MaskGCT——开源文本转语音模型,可模仿任何人说话声音

前期介绍过很多语音合成的模型&#xff0c;比如ChatTTS&#xff0c;微软语音合成大模型&#xff0c;字节跳动自家发布的语音合成模型Seed-TTS。其模型随着技术的不断发展&#xff0c;模型说话的声音也越来越像人类&#xff0c;虽然 seed-tts 可以进行语音合成等功能&#xff0c…

java全栈day16--Web后端实战(数据库)

一、数据库介绍 二、Mysql安装&#xff08;自行在网上找&#xff0c;教程简单&#xff09; 安装好了进行Mysql连接 连接语法&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroot -p密码 方法二&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroo…

geoserver 瓦片地图,tomcat和nginx实现负载均衡

在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;GeoServer作为一个强大的开源服务器&#xff0c;能够发布各种地图服务&#xff0c;包括瓦片地图服务。为了提高服务的可用性和扩展性&#xff0c;结合Tomcat和Nginx实现负载均衡成为了一个有效的解决方案。本文将详细…