java多线程等待唤醒机制、 Lambda表达式、单例设计模式
1.多线程等待唤醒机制
1.1等待唤醒机制概述:
- 等待唤醒机制是实现两个或多个线程在执行任务过程中相互配合相互协作的一种技术。
- 典型应用场景:生产者与消费者案例。
- 线程等待与唤醒:
- 线程等待与唤醒又称为线程间通信。
- 等待唤醒机制是实现两个或多个线程在执行任务过程中相互配合相互协作的一种技术。
- 线程等待与唤醒相关的方法:
- void wait();等待,让线程释放CPU的使用权进入无限等待状态。
- void notify();唤醒,随机唤醒一个正在等待状态的线程,让其进入可运行状态。
- void notifyAll();唤醒所有,唤醒所有正在等待状态的线程,让其进入可运行状态。
- 以上三个方法必须由锁对象调用且必须在同步代码块或同步方法中调用。
- java.lang.IllegalMonitorStateException:非法监视器状态异常=>
2.1.等待唤醒机制案例:
public class Resource {
public String name;
public String gender;
}
public class ProductThread implements Runnable {
private Resource r;
public ProductThread(Resource r) {
this.r = r;
}
int index=0;
@Override
public void run() {
while (true){
//使用同步代码块实现线程安全
synchronized (r){
if(index % 2 == 0){
r.name="陈奕迅";
r.gender="男";
}else {
r.name="lily";
r.gender="女";
}
try {
//唤醒消费线程消费数据
r.notify();
//让当前线程进入等待状态
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//索引++
index++;
}
}
}
public class ConsumerThread implements Runnable {
private Resource r;
public ConsumerThread(Resource r) {
this.r = r;
}
@Override
public void run() {
while(true){
//使用同步代码块实现线程安全
synchronized (r){
//判断生成线程是否已经产生了数据
if(r.name == null){
//让当前线程进入等待状态
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(r.name+"="+r.gender);
try {
//唤醒生成线程
r.notify();
//让当前线程进入等待状态
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建资源对象
Resource r=new Resource();
//创建生产和消费线程
Thread pt = new Thread(new ProductThread(r));
Thread ct = new Thread(new ConsumerThread(r));
//开启线程
pt.start();
ct.start();
}
}
2.2. sleep和wait的区别:(面试题)
- sleep方法调用需要指定时间,wait方法调用可以指定时间也可以不指定。
- sleep方法不会释放锁对象,wait方法会释放锁对象。
- sleep方法可以在任意类任意方法中调用,wait方法必须通过锁对象在同步代码块或同步方法中调用。
- sleep方法不需要被唤醒,wait方法如果没有指定时间则需要被唤醒。
3.1 Lambda表达式概述:
- Lambda表达式概述:JDK1.8新特性,用来简化匿名内部类。
- Lambda表达式思想:只专注做什么,而不是怎么做。
- Lambda表达式的标准格式:
- (参数列表)- >{方法体}
- 小括号就是方法的参数列表
- ->新语法,代表动作执向
- 大括号就是方法体
- Lambda表达式的使用前提:必须有接口且接口中有且只有一个抽象方法。
案例1:开启一个线程执行一个任务,任务是在控制台中输出: 我用java书写人生
public class LambdaDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我用java书写人生");
}
}).start();
//使用Lambda表达式简化匿名内部类
new Thread(()->{
System.out.println("我用java书写人生");
}).start();
}
}
案例2: Lambda之Comparator接口使用
1. 定义一个学生类,成员变量有:姓名,年龄,成绩。
2. 创建多个学生对象添加到集合中,对集合的学生对象进行排序(升序和降序)
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
1. 定义一个学生类,成员变量有:姓名,年龄,成绩。
2. 创建多个学生对象添加到集合中,对集合的学生对象进行排序(升序和降序)
*/
public class LambdaDemo01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> list=new ArrayList<>();
list.add(new Student("马化腾",18,100));
list.add(new Student("马云",21,99));
list.add(new Student("雷军",24,96));
list.add(new Student("李彦宏",35,87));
/*//对集合学生排序,按成绩升序排序
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getScore()-o2.getScore();
}
});*/
//使用Lambda表达式的标准格式简化
Collections.sort(list,(Student o1, Student o2)->{
return o1.getScore()-o2.getScore();
});
for(Student student:list){
System.out.println(student);
}
}
}
案例3:使用Lambda标准格式(无参无返回)
给定一个厨子 Cook 接口,内含唯一的抽象方法 makeFood ,且无参数、无返回值。如下:
interface Cook{
void makeFood();
default void xx(){}
}
在下面的代码中,请使用Lambda的标准格式调用 invokeCook 方法,打印输出“吃饭啦!”字样:
public class LambdaDemo02 {
public static void main(String[] args) {
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭啦!");
}
});
//使用Lambda的标准格式调用 invokeCook 方法,打印输出“吃饭啦!”
invokeCook(()->{
System.out.println("吃饭啦!");
});
}
private static void invokeCook(Cook cook){
cook.makeFood();
}
}
interface Cook{
void makeFood();
}
案例4: 使用Lambda标准格式(有参有返回)
给定一个计算器 Calculator 接口,内含抽象方法 calc 可以将两个int数字相加得到和值:
interface Calculator{
int calc(int a,int b);
}
在下面的代码中,请使用Lambda的标准格式调用 invokeCalc 方法,完成120和130的相加计算:
interface Calculator{
int calc(int a,int b);
}
public class LambdaDemo {
public static void main(String[] args) {
//使用匿名内部类调用
invokeCalc(120, 130, new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
invokeCalc(120,130,(int a,int b)->{
return a+b;
});
//请使用Lambda的标准格式调用 invokeCalc 方法,完成120和130的相加计算:
}
private static void invokeCalc(int a,int b,Calculator calculator){
int result = calculator.calc(a,b);
System.out.println("结果是:"+result);
}
}
3.2 Lambda的省略格式:
- 在Lambda标准格式的基础上,使用省略写法的规则是:
- (参数列表)-> {方法体}
- 参数的数据类型可以省略。
- 如果参数列表中只有一个参数时,小括号可以省略。
- 如果方法体中只有一条语句时,大括号也可以省略,如果省略了大括号则return语句和分号必须省略。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
1. 定义一个学生类,成员变量有:姓名,年龄,成绩。
2. 创建多个学生对象添加到集合中,对集合的学生对象进行排序(升序和降序)
*/
public class LambdaDemo01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> list=new ArrayList<>();
list.add(new Student("马化腾",18,100));
list.add(new Student("马云",21,99));
list.add(new Student("雷军",24,96));
list.add(new Student("李彦宏",35,87));
/*//使用Lambda表达式的标准格式简化
Collections.sort(list,(Student o1, Student o2)->{
return o1.getScore()-o2.getScore();
});*/
//使用Lambda表达式的简化格式
Collections.sort(list,( o1, o2)-> o1.getScore()-o2.getScore());
for(Student student:list){
System.out.println(student);
}
}
}
3.3 使用lambda表达式对集合进行遍历 :
import java.util.*;
import java.util.function.Consumer;
public class LambdaDemo01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> list=new ArrayList<>();
list.add(new Student("马化腾",18,100));
list.add(new Student("马云",21,99));
list.add(new Student("雷军",24,96));
list.add(new Student("李彦宏",35,87));
//使用匿名内部类遍历集合
list.forEach(new Consumer<Student>() {
@Override
public void accept(Student student) {
System.out.println(student);
}
});
System.out.println("------------------");
//使用Lambda的标准格式遍历集合
//public void forEach(Consumer<? super E> action)
list.forEach((Student student)->{
System.out.println(student);
});
System.out.println("--------------------");
//使用Lambda的简化格式遍历集合
//void accept(T t);
list.forEach(student -> System.out.println(student));
System.out.println("-----------遍历set集合----------");
Set<Integer> set=new HashSet<>();
Collections.addAll(set,34,35,45,56,576,7,34);
set.forEach(num -> System.out.println(num));
System.out.println("-----------遍历map集合------------");
//BiConsumer<? super K, ? super V> action
// void accept(T t, U u);
Map<String,String> map=new HashMap<>();
map.put("name","jack");
map.put("gender","男");
map.forEach((key,value)-> System.out.println(key+"="+value));
}
}
4.单例模式:
- 单例实现方式(面试)
单例模式概述:
- 类在程序运行过程中有且只有一个对象。
- 类必须私有构造方法。
- 类必须要提供一个静态方法返回该类的单例对象。
单例实现方式:
- 懒汉式模式:在第一次访问该类的单例对象时,如果还没有创建出来,则创建,如果已经创建了 则直接返回。(线程不安全的)
- 饿汉式模式:类加载的同时就创建该类的单例对象。(线程安全的)
/*
饿汉式模式:类加载的同时就创建该类的单例对象
*/
public class Singleton {
private static Singleton s=new Singleton();
//私有构造方法
private Singleton(){}
//类必须提供一个静态方法返回该类的单例对象
public static Singleton getInstance(){
return s;
}
}
public class SingleDemo {
public static void main(String[] args) {
System.out.println(Singleton.getInstance());
System.out.println(Singleton.getInstance());
System.out.println(Singleton.getInstance());
}
}
/*
aaa@qq.com
aaa@qq.com
aaa@qq.com
*/
/*
懒汉式模式:在第一次访问该类的单例对象时,如果还没有创建出来,则创建,如果已经创建了 则直接返回。
*/
public class Singleton {
private static Singleton s=null;
//私有构造方法
private Singleton(){}
//类必须提供一个静态方法返回该类的单例对象
public static Singleton getInstance(){
//判断s是否已经创建了
if(s == null){
s = new Singleton();
}
return s;
}
}
静态同步方法使用线程安全:不推荐使用,效率低
使用同步代码块使用线程安全
/*
懒汉式模式:在第一次访问该类的单例对象时,如果还没有创建出来,则创建,如果已经创建了 则直接返回。
*/
public class Singleton {
private static Singleton s=null;
//私有构造方法
private Singleton(){}
//类必须提供一个静态方法返回该类的单例对象
public static Singleton getInstance(){
if(s == null){
synchronized (Singleton.class){
//判断s是否已经创建了
if(s == null){
s = new Singleton();
}
}
}
return s;
}
}
上一篇: 安装kali遇到问题及解决
下一篇: java基础笔记