目录
线程池的概念
- 一个可以容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而过多消耗资源
线程池(ExecutorService)的特点
- 降低资源消耗。传统情况创建线程就需要资源,而线程池可以使线程重复利用,减少线程的创建/销毁次数,从而降低资源消耗。
- 提高响应速度。当任务到达时,可以调用空闲线程,不需要等待创建线程。
- 提高可管理性。同时处理多个线程,会超出cpu承载范围,造成系统崩溃,线程池可以调度线程和调整工作线程的数目
线程池的创建方法
Executors
- newCachedThreadPool():根据需求创建对象,有空闲线程的时候使用空闲线程,没有的时候创建新线程。
- newFixedThreadPool(int nThread):根据给定参数创建固定最大数量的线程数,如果需要的线程超过nThread,则后面的线程等前面线程结束才可以执行
- newScheduledThreadPool(int corePoolSize)(不常用):创建一个线程池,可以延时运行或者定时运行线程,也可以设置和newFixedThreadPool相同效果,注意:要使用延迟/定时功能,返回的对象应该要用ScheduledExecutorService类型,使用schedule(Runnablecommand,long delay,TimeUnit unit)设置延时时间执行的任务
- newSingleThreadExecutor()(不常用):创建一个单线程的线程池
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行任务");
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
System.out.println(Thread.currentThread().getName()+"结束执行任务");
}
};
pool.submit(task);//第一个线程
pool.submit(task);//由于第一个线程还没结束,线程池中没有空闲线程,线程池创建一个新线程
Thread.sleep(3000);//休眠,等前两个线程结束
System.out.println("---------------------------");
pool.submit(task);//线程池中有空闲线程,使用空闲线程
pool.submit(task);//线程池中有空闲线程,使用空闲线程
pool.submit(task);//由于没有线程,线程池创建一个新的线程
pool.shutdown();//关闭线程池,关了之后再用会报错
}
常用API
返回类型 | 方法 | 描述 |
Future<?> | submit(Runnable task) | 提交Runnable类型任务执行并返回表示该任务的Future |
void | shutdown() | 启动有序关闭,先前提交的任务会继续执行,但不能添加新任务 |
Properties属性集
xxx.properties类型的文件是配置文件,另外还有xxx.xml也是配置文件,其作用是存储某些需要动态改变的值,比如文件路径,密码等。Properties类就是从这种后缀的文件中读取信息,并以类似Hashtable的方式返回,只不过键和值都是String类型的
使用方法
public static void main(String[] args) throws IOException, InterruptedException {
Properties pros = new Properties();//先创建一个Properties对象
pros.load(new FileReader("test.properties"));//调用load(Reader reader)方法找到配置文件的路径
String path = pros.getProperty("path");//使用getProperty方法,返回一个字符串
System.out.println(path);//到这里是读取.properties文件内容
//写入.properties文件
pros.setProperty("age","18");//先将key-value键值对加入属性集
pros.store(new FileOutputStream("test.properties"),"注释");//使用输出流将原来有的键值对和新增的键值对写入配置文件,这里可以使用追加模式
}
.properties文件里的内容
#\u6CE8\u91CA #Mon Jul 18 15:18:58 CST 2022 path=eee age=18
.properties文件里的注释使用#号,用unicode字符集编写,下面会自动添加一个最后更改时间,和Map一样,如果键有重复,使用最后添加的键
在IDEA中,点击File->Settings->Editor->File Encodings,下方关于.properties文件有相关设置,可以选择.properties的默认编码格式,后面可以勾选是否将写入的内容自动转化成ASCII码,勾选上的话,字节流写入文件时,不管什么格式写入文件,都会转化成ASCII码;字节流读取时,ASCII码会自动转义成对应的文字,这样不管是UTF-8还是GBK格式都不会乱码。
获得资源路径 Resource
.java文件编译后在out文件夹下,并且会自动去掉src文件夹,如果是非.class文件,必须要放到src里面才可以在编译后被放进out文件夹下的工程目录中,所以用相对路径不能按照有src的方法来找,要先获得.class文件所在父文件夹(类路径),然后根据这个文件夹往上或者往下找对应的文件夹或者文件。
out的构成:out->producetion->工程->(包->).class文件
类路径:src编译后的路径
- Demo.class.getResource("config.txt").getPath(),返回Demo.class文件所在的文件夹下的config.txt文件路径(config.txt和Demo.class文件同级)(编译前config.txt在Demo所在的包下面)
- Demo.class.getResource("../config.txt").getPath()返回Demo.class文件所在的文件夹上一级文件夹下的config.txt文件路径(config.txt和Demo.class文件的父文件夹同级)
- Demo.class.getClassLoader().getResource("config.txt").getPath()返回Demo.class文件所在工程下的config.txt(编译前config.txt在Demo所在的src下面)
lambda表达式
满足以下三个条件即可使用lambda表达式
- 在匿名内部类中实现接口
- 这个接口是函数式接口:接口中只有一个抽象方法 @FunctionalInterface注解
- 作为方法参数使用
语法
(参数) -> {要实现的抽象方法的方法体}
- 简化1:如果方法有参数,参数类型可以省略
- 简化2:如果方法中只有一句代码,那么return,分号,{}可以一起省略
Arrays.sort(arr,(o1,o2)/*如果有参数,参数类型可以省略*/ -> o2-o1/*如果方法中只有一句代码,那么return,分号,{}可以一起省略*/);
- 简化3:如果方法只有一个参数,()可以省略
总结
- 线程池可以提高程序运行效率
- 路径等易改变的变量应存在Properties属性集中
- 相对路径要注意改用.class在的目录,否则在别的地方运行容易出错
- 在相对路径的表达中./代表当前目录,../代表当前目录的上一级目录,../../代表当前目录的上上级目录
- lambda只能省略匿名内部类里那些确定不变的部分