一。线程通信: 共享资源
wait() notify()
notify() - 每次只能唤醒一个线程, 只能唤醒等待时间久的那个线程
notifyAll() - 唤醒所有正在等待的线程
wait() -> 只能被notify() 或者 notifyAll() 唤醒
wait(long) -> 到时间以后, 自动醒来
1.包子铺和吃货的线程通信
public class Baozi {
String pier;
String xiang;
boolean flage=false;//初始化包子资源状态
}
---------------------------------------------
public class Baozipu extends Thread {
private Baozi bz;
public Baozipu(String name, Baozi bz) {
super(name);
this.bz = bz;
}
@Override
public void run() {
int count = 0;
//造包子
while (true) {
synchronized (bz) {
//有包子,先等待不造
if (bz.flage) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没包子,造包子
if (!bz.flage) {
System.out.println(Thread.currentThread().getName() + "包子铺开始生产包子");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count % 2 == 0) {
bz.pier = "冰皮";
bz.xiang = "无仁";
} else {
bz.pier = "薄皮";
bz.xiang = "牛肉大葱";
}
count++;
//造出包子改flage
System.out.println("包子造好了:" + bz.pier + bz.xiang);
System.out.println("吃货来吃吧");
bz.flage = true;
//唤醒吃货线程
bz.notify();//唤醒等待时间最长的那个线程
}
}
}
}
}
--------------------------------------------------
public class Chihuo extends Thread {
private Baozi bz;
public Chihuo(String name, Baozi bz) {
super(name);
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
//没包子,等包子
//System.out.println(Thread.currentThread().getName() + "在等包子");
if (!bz.flage) {
System.out.println(Thread.currentThread().getName() + "在等包子");
try {
bz.wait(500);//0.5s后自动醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bz.flage) {
//有包子吃包子
System.out.println(Thread.currentThread().getName() + "吃货在吃" + bz.pier + bz.xiang + "包子");
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//吃完把flage改false
bz.flage = false;
//释放做包子,释放其他吃货
bz.notifyAll();//唤醒全部线程
}
}
}
}
}
-----------------------------------------------
public class Text {
public static void main(String[] args) {
Baozi bz=new Baozi();
Baozipu bzp=new Baozipu("正新bzp",bz);
Chihuo ch=new Chihuo("齐天大圣",bz);
Chihuo ch1=new Chihuo("天蓬元帅",bz);
bzp.start();
ch.start();
ch1.start();
}
}
二。线程池: Executors 工厂类中的方法
线程池执行任务的API:
1.submit(Runnable/Callable)
2.execute(Runnable)
newCachedThreadPool(): 创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
newFixedThreadPool(int nThreads): 创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
newScheduledThreadPool(int corePoolSize): 创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
newSingleThreadExecutor(): 创建一个使用从无界队列运行的单个工作线程的执行程序。
- newFixedThreadPool
1.Callable 创建线程任务
public class Demo02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建一个固定线程数量的线程池, 创建好线程池的时候, 就已经有了三个线程对象
ExecutorService pool = Executors.newFixedThreadPool(3);
// 通过线程任务, 来获得线程对象并且直接开始线程
Callable run = new Callable<Date>() {
@Override
public Date call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
Thread.sleep(10000);
return new Date();
}
};
// 将线程任务交给线程池 -- 线程对象可以反复使用
Future<Date> f = pool.submit(run);//获取Callable中call方法返回值
Date d = f.get(); //获取submit的返回值。此处为线程池中第一个线程执行完的时间。而且get()有阻塞的作用
System.out.println(d);
/*try {//此处为get()的用法
Date date = f.get(3, TimeUnit.SECONDS);
System.out.println(date);
} catch (TimeoutException e) {
System.out.println("结果超时了");
}*/
System.out.println("主线程继续");
// 如果没有关闭线程池, 线程对象依然在, 程序就不会结束
// 手动关闭线程池 - 会自动将里面的线程对象销毁
pool.shutdown();
}
}
Callable对象只能在 : Future<Date> f = pool.submit(Callable);
f.get() -> 得到call方法的返回值
可能会遇到阻塞
f.get(long, TimeUnit.xx) -> 超时继续
-----------------------------------------------------------------
2.Runnable 创建线程任务
public class Demo {
public static void main(String[] args) {
// 通过线程任务, 来获得线程对象并且直接开始线程
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// 创建一个固定线程数量的线程池, 创建好线程池的时候, 就已经有了三个线程对象
ExecutorService pool = Executors.newFixedThreadPool(3);
// 将线程任务交给线程池 -- 线程对象可以反复使用
pool.execute(run);//开启线程池可以用pool.execute(run);但是execute里只能传Runnable
Future f = pool.submit(run); // f = null
pool.submit(run);//开启线程池也可以用pool.submit(run);
pool.submit(run);
// 如果没有关闭线程池, 线程对象依然在, 程序就不会结束
// 手动关闭线程池 - 会自动将里面的线程对象销毁
pool.shutdown();
}
}
三。Lambda表达式: JDK1.8 函数式编程思想
面向对象思想: 什么对象, 做什么, 结果是什么
函数式编程思想: 强调做什么,而不是以什么形式做。
-
语法: (参数列表) -> {一些代码}
使用Lambda前提:
1.实现一个接口
2.接口中只有一个抽象方法
3.接口对象是作为方法参数使用的(参数列表) -> {一些代码}
(参数列表): 表示要重写的抽象方法的参数列表
-> : 固定语法, 指向/传递的意思
{一些代码}: 要重写的方法体
Lambda 取代匿名内部类的
匿名内部类: 本质还是类, 编译后也会生成字节码文件, 运行时也要加载
Lambda: 本质是一个函数, 编译后不会有字节码文件, 直接从内存中获取
效率更高 -
可推导即可省略:
1.() 中的参数类型, 可以省略, 如果有多个参数, 每个参数类型都要一起省略
2.{} 中如果只有一行代码, 不论这个方法有没有返回值, 那么[{} return ;] 可以省略
{} return ; 必须一起省略
3.() 中如果只有一个参数, () 可以省略, 和类型一起省略
() 中如果没有参数, 必须写 ()
1.Thread的Lambda表达式
public class Lambda1 {
public static void main(String[] args) {
//Thred
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("哈哈");
}
});
t.start();
//lambda简化
//方法((参数列表){方法});
new Thread(()->{
System.out.println("哈哈");
}).start();
//继续优化
new Thread(()->
System.out.println("哈哈")//方法只有一行省略{}和; 当()里没有参数()不可以省略
).start();
}
}
2.Collections.sort的Lambda表达式
public class lambda2 {
public static void main(String[] args) {
List<String>lis=List.of("99","10","100");
// lis=new ArrayList<>();数组简化写法
List<String> list=new ArrayList<>();
list.add("33");
list.add("222");
list.add("1");
Collections.sort(list, new Comparator<String>() {//匿名内部类形式
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
System.out.println(list);
//lambda简化
Collections.sort(list,(String o1, String o2) -> {//lambda形式
return o1.length() - o2.length();
});
System.out.println(list);
//继续简化
Collections.sort(list,( o1, o2)-> o1.length()-o2.length() //可以省略()内参数类型,只有一行方法,省略{}和;和return
);
}
}
3.自定义接口,将接口对象作为方法参数, 然后使用Lambda表达式
demo1
public interface Myinterface {
public int sum(int a,int b);
}
-----------------------------------
public class Text {
public static void main(String[] args) {
int s = Myin((int a, int b) -> {//可以省略return和;和{}
return a + b;
}, 3, 2);
System.out.println(s);
//继续简化
int s1 = Myin(( a, b) -> a + b
, 3, 2);
}
public static int Myin(Myinterface in, int a, int b) {// 需要将接口对象作为方法参数, 才可以使用Lambda表达式
return in.sum(a, b);
}
}
demo2
@FunctionalInterface//函数接口的注解
public interface Myinterface {
int play(int a);//接口中只有一个抽象方法叫函数式接口。但还可以有默认、静态、私有方法
}
-----------------------------------------------
public class Text {
public static void main(String[] args) {
int t= myplay((int a)->{//()中只有一个参数时可以省略()
return a+3;
},3);
//简化
int t2=myplay(a->a+3,3);
System.out.println(t+":"+t2);
}
public static int myplay(Myinterface in,int a){//接口对象作为参数
return in.play(a);
}
}
4.函数式接口: 接口中只有一个抽象方法, 默认方法\静态方法\私有方法 随意
@FunctionalInterface -> 检测是不是函数式接口
注解: JDK 1.5 属于程序的一部分, 可以取代一部分配置信息
@Override -> 检测方法是不是重写
@SuppressWarnings -> 压制警告
@Deprecated -> 标记一个类或者方法或者变量, 过时的
@FunctionalInterface -> 检测一个接口是不是函数式接口
四。 可变长参数:
取代了数组 int[] arr -> int… arr
JDK1.5后, 修改为可变长参数
1.方法中, 除了可变长参数, 还有别的参数, 可变长参数必须放在参数列表最后
2.而且一个方法中, 只能有一个可变长参数
public class Demo01 {
public static void main(String[] args) {
sum(2, 4);
sum(1,2,3,4,5);
sum(1);
sum();
sum(1, 2, 3);
sum(1, 2, 3, 4);
}
/*
传入n个整数值, 将传入的整数相加求和, 并返回
*/
/*public static int sum(int a, int b) {
return a + b;
}*/
// JDK1.5之前, 不确定参数个数时, 就传入数组类型
/*@Deprecated
public static int sum(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}*/
/*
JDK1.5后, 修改为可变长参数
方法中, 除了可变长参数, 还有别的参数, 可变长参数必须放在参数列表最后
而且一个方法中, 只能有一个可变长参数
*/
public static int sum(int... arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
public static void method1(int a, String... ss) {
}
}
五。JUnit: 单元测试 -> 以方法为单位, 可以取代主方法
System.out.println(); -> 打桩测试
Debug -> 断点测试
JUnit -> 单元测试
别人写好的代码, 我们想要使用
1.将别人写好的代码工程, 打包 jar 文件
是将编译后的字节码文件打包的
2.在自己的工程中 关联这个jar文件 -> 添加依赖
3.代码中直接使用 import
JUnit使用步骤:
1.在工程中创建一个文件夹lib
2.将2个jar包复制到lib中
3.选择jar包 右键 -> Add as Library… 默认添加即可
4.写一个测试类, 在其中写一个方法[不需要返回值,不需要参数]
5.在方法上添加注解 @Test
6.运行这个方法即可
JUnit常用注解:
@Test: 测试的方法, 可以直接运行
@Before: 在测试方法之前调用的
@After: 在测试方法之后调用的