第一章 阻塞队列
java.util.concurrent.BlockingQueue 阻塞队列顶层接口
方法
boolean add(E e); 有空间 添加成功 返回true 没空间抛异常
boolean offer(E e); 有空间 添加成功 返回true 没空间 返回false
void put(E e); 有空间 添加成功 返回true 没空间等待
E take(); 获取元素 如果没有 等待
常用实现类
ArrayBlockingQueue
有界
LinkeBlockingQueue
可选有界
SynchronousQueue
有界 一个元素都不存
代码演示:
public class Test03 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> b = new ArrayBlockingQueue<>(3);
//有容量的情况下 用那个方法都可以
b.add("aaa");
b.offer("abc");
b.put("ccc");
System.out.println(b);
//满了后
// b.add("sss"); //抛异常
boolean b1 = b.offer("qqq"); //不抛异常返回false
System.out.println(b1);
//满了等待
//b.put("ee");
System.out.println("...");
//获取并移除
String take = b.take();
System.out.println(take); // aaa
System.out.println(b.take()); // abc
System.out.println(b.take()); // ccc
System.out.println(b);
//没有元素 等待
System.out.println(b.take());
System.out.println("game over");
}
}
第二章 线程池
概述
创建线程每次都要和操作系统进行交互,线程执行完任务后就会销毁,
如果频繁的大量去创建线程肯定会造成系统资源开销很大,降低程序的运行效率。
线程池思想就很好的解决了频繁创建线程的问题,
我们可以预先创建好一些线程,把他们放在一个容器中,需要线程执行任务的时候,从容器中取出线程,任务执行完毕后将线程在放回容器。
使用线程池好处:
1. 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,
使用线程池可以进行统一的分配、调优和监控
Executors线程池工具类
作用: 快速创建线程池对象
方法:
static ExecutorService newFixedThreadPool(int nThreads); 创建一个可重用固定线程数的线程池。
static ExecutorService newCachedThreadPool(); 创建一个可根据需要创建新线程的线程池;
static ExecutorService newSingleThreadExecutor(); 创建一个使用单个 worker 线程的 Executor;
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
ExecutorService 线程池接口
方法:
Future<?> submit(Runnable task); 提交线程执行的任务,方法将返回null,因为run()方法没有返回值。
<T>Future<T>submit(Callable<T> task); 提交线程执行的任务,返回Future接口对象。
void shutdown(); 关闭线程池,但是要等所有线程都完成任务后再关闭,但是不接收新任务。
使用Callable完成异步计算
Callable接口
线程执行的任务接口,类似于Runnable接口。
接口方法`public V call()throw Exception`
线程要执行的任务方法
比起run()方法,call()方法具有返回值,可以获取到线程执行的结果。
代码演示:
public class MyCall implements Callable<Integer> {
private int num;
public MyCall(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <=num; i++) {
sum +=i ;
}
return sum;
}
}
public class MyRun implements Runnable{
private int i ;
public MyRun(int i){
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了任务"+i);
}
}
public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(2);
for (int i = 1; i <= 10 ; i++) {
pool.submit(new MyRun(i));
}
Future<Integer> submit = pool.submit(new MyCall(1000));
Future<Integer> b = pool.submit(new MyCall(100));
//获取call方法 返回的值
System.out.println(submit.get());
System.out.println(b.get());
//执行完任务 然后关闭线程池
pool.shutdown();
//已提交的任务执行完 关闭线程池 未提交的任务就不执行了
pool.shutdownNow();
}
}
ThreadPoolExecutor
我们可以使用ThreadPoolExecutor去创建线程池。
ThreadPoolExecutor最完整的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
corePoolSize: 核心线程数量,线程池维护线程的最少数量
maximumPoolSize: 线程池维护线程的最大数量
keepAliveTime: 线程池除核心线程外的其他线程的最长空闲时间,超过该时间的空闲线程会被销毁
unit: keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
workQueue: 线程池所使用的任务缓冲队列
threadFactory: 线程工厂,用于创建线程,一般用默认的即可
handler: 线程池对拒绝任务的处理策略
当线程池任务处理不过来的时候(什么时候认为处理不过来后面描述),可以通过handler指定的策略进行处理,ThreadPoolExecutor提供了四种策略:
1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;也是默认的处理方式。
2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
代码演示:
public class Test {
private static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
//此线程仅用于演示效果 可以让线程池中的线程任务快速执行完毕
new Thread(new Runnable() {
@Override
public void run() {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s = sc.nextLine();
if ("stop".equals(s)) {
flag = false;
break;
}
}
}
}).start();
//创建线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,
3,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(5),
// Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//创建10个线程任务执行
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
pool.execute(new MyRunnable((i + 1) + ""));
}
}
static class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行任务" + name);
while (flag) {
}
}
}
}
第三章 File类
3.1 概述
java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
File类将文件,文件夹和路径封装成了对象,提供大量的方法来操作这些对象
3.2 File类的静态成员变量
static String pathSeparator 与系统有关的路径分隔符。
Window操作系统,分隔符是分号。
Linux操作系统,分隔符是冒号。
static String separator 与系统有关的默认名称分隔符。
Window操作系统,名称分割符号为 \。
Linux操作系统,名称分隔符号为 /。
代码演示:
//路径分隔符
System.out.println(File.pathSeparator); //win ; linux :
//名称分隔符
System.out.println(File.separator); // win \ linux /
//写一个既可以在win运行 也可以在Linux上运行的通用路径
String path2 = "work"+File.separator+"abc"+File.separator+"1.txt";
System.out.println(path2);
3.3 绝对路径和相对路径
绝对路径:从盘符开始的路径,这是一个完整的路径,绝对路径具有唯一性。
相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
代码演示:
String path = "d:\\aaa\bbb\ccc\a.txt";//带盘符 绝对路径
String path2 = "a.txt"; //相对路径
3.4 File的构造方法
public File(String pathname); 根据给定的路径创建File对象
File(String parent, String child); 根据给定的父路径 和子路径创建File对象
File(File parent, String child); File类型的父路径 字符串的子路径
注意:
1. File类的构造方法 不检测路径是否存在
2. 注意Windows不区分大小写
3. 路径可以是文件 也可以是文件夹 但是只是创建对象
代码演示:
// 文件路径名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname);
System.out.println(file1);
// 通过父路径和子路径字符串
String parent = "d:\\aaa";
String child = "bbb.txt";
File file2 = new File(parent, child);
System.out.println(file2);
// 通过父级File对象和子路径字符串
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file3 = new File(parentDir, child);
System.out.println(file3);
3.5 File类的获取方法
public String getAbsolutePath(): 返回此File的绝对路径名字符串。
public String getPath(): 将此File转换为路径名字符串。
public String getName(): 返回由此File表示的文件或目录的名称。
public long length(): 返回由此File表示的文件的长度。
public File getParentFile(): 返回由此File表示的文件或目录的父目录,如果没有父目录,返回null
代码演示:
File f = new File("d:/aaa/bbb.java");
System.out.println("文件绝对路径:"+f.getAbsolutePath());
System.out.println("文件构造路径:"+f.getPath());
System.out.println("文件名称:"+f.getName());
System.out.println("文件长度:"+f.length()+"字节");
System.out.ptintln("文件路径的父路径"+f.getParentFile());
3.6 File类判断方法
public boolean exists(): 此File表示的文件或目录是否实际存在。
public boolean isDirectory(): 此File表示的是否为目录。
public boolean isFile(): 此File表示的是否为文件。
代码演示:
File file2 = new File("d:\\2development\\abc\\aa\\bb\\cc\\dd");
//判断路径是否存在 存在返回true 不存在返回false
boolean b = file2.exists();
System.out.println(b);
//判断是否是文件 是文件返回true 是文件夹返回false 路径不存在 返回false
boolean b3 = file2.isFile();
System.out.println(b2);
if (file2.exists()){
if (file2.isFile()){
System.out.println("文件");
}else{
System.out.println("文件夹");
}
}else{
System.out.println("路径不存在");
}
3.7 File类创建删除功能的方法
public boolean delete() : 删除由此File表示的文件或目录。 不进回收站,直接删除。
如果删除的是文件 即使文件中有数据 也可以删除
如果删除的是文件夹 必须是空文件夹 才能删除
注意:
java删除是永久性删除 不走回收站
操作有风险 运行需谨慎
3.8 File类目录遍历方法
public File[] listFiles(): 返回一个File数组,表示该File目录中的所有的子文件或目录
第四章 方法递归
递归:指在当前方法内调用自己的这种现象
public static void a(){
a();
}
代码演示:
public class Test01_File{
public static void main(String[] args) {
//递归计算1 - n的和
int sum = getSum(100);
System.out.println(sum);
//递归求阶乘:所有小于及等于该数的正整数的积。
int value = getValue(10);
System.out.println(value);
//遍历 D:\aaa 目录下的所有文件和所有的子目录。
// 创建File对象
File dir = new File("D:\\aaa");
// 调用打印目录方法
printDir(dir);
}
public static int getSum(int n){
if (n == 1){
return 1;
}else {
return n +getSum(n-1);
}
}
public static int getValue(int n){
if (n == 1){
return 1;
}
return n * getValue(n-1);
}
public static void printDir(File dir) {
System.out.println(dir);
// 获取子文件和目录
File[] files = dir.listFiles();
// 循环打印
for (File file : files) {
//判断是文件,直接输出
if (file.isFile()) {
System.out.println(file);
} else {
// 是目录,继续遍历,形成递归
printDir(file);
}
}
}
}