11-21
1.多线程的实现方式2-----要实现Runable接口
1.)自定义一个类实现Runable接口
2.)实现接口里面的run方法
3.)在main用户线程中创建当前这个类的实例--->资源类对象
4.)创建线程类Thread对象,然后将3.)资源类对象,作为此参数传递,启动线程
举例:
自定义一个类shixRunable接口
实现里面的run方法
public class MyRunable implements Runable{
@Override
public void run() {
for(int x = 0 ; x < 200 ; x++){
//Thread类的静态方法public static Thread currentThread()
//返回当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
创建类的实例
public class ThreadDemo {
public static void main(String[] args) {
//创建当前这个类对象 "资源类"
MyRunnable my = new MyRunnable() ;
//创建两个线程,同时可以给线程设置名称
//public Thread(Runnable target,String name)
//Thread类--->代理角色
Thread t1 = new Thread(my,"t1") ;
Thread t2 = new Thread(my,"t2") ;
//分别启动线程
t1.start();
t2.start();
}
2.电影三个窗口同时出售100张票,展示"第X个窗口正在出售第X张票"
public class SellTicketThread extends Thread{
//100张票
private static int tickets = 100 ;
//t1,t2,t3都要并发执行
@Override
public void run() {
//模拟一直有票
while (true){
//模拟真实场景,线程睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果tickets>0
if(tickets>0){
System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
}
public class SellTicketTest {
public static void main(String[] args) {
//创建三个线程类对象
SellTicketThread st1 = new SellTicketThread() ;
SellTicketThread st2 = new SellTicketThread() ;
SellTicketThread st3 = new SellTicketThread() ;
//线程名称
st1.setName("窗口1") ;
st2.setName("窗口2") ;
st3.setName("窗口3") ;
//启动线程
st1.start();
st2.start();
st3.start();
}
}
3.Java设计模式 结构型设计--->代理模式--->静态代理3
代理设计模式
静态代理
代理类帮助真实角色完成一些事情,代理类和真实角色实现同一个接口
动态代理
jdk动态代理---->只要有一个接口---->反射的方式完成针对接口的对象的创建
4.使用多线程的方式 完成电影院三个窗口出售100张票,可能出现问题(安全问题)
解决线程安全问题
1.)是否是多线程环境---->是
2.)是否有共享数据---->是存在的
3.是否有多条语句对共享数据操作
买票----tickets:多条语句同时操作
synchronized(锁对象){
多条语句对共享数据操作
}
举例:
synchronized(SellTicket.class){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+
"正在出售第"+(tickets--)+"张票");
}
}
锁对象:可以是任意java类对象,但是多个线程必须使用的同一个锁对象,否则锁不住
5.同步锁synchronized----->同步方法:将synchronized声明到方法上
什么是同步机制?
同步机制:
多个线程在并发的使用公共的某个变量(被共用),保证同步(保证安全),某个线程在当前这个数据进行持有的时候,其他线程是不能访问的----通过synchronized同步代码块就可以解决线程安全问题
synchronized(监视器){
}
1)需要将多条语句对共享数据进行操作使用同步代码块包裹起来
2)同步机制里面的监视器,俗称"锁",可以是任意Java类对象,多个线程必须使用同一个监视器
或者使用同步方法(斐济静态)---锁对象 this
静态的同步方法 ---> 锁对象 当前类名.class 字节码文件对象
同步机制:
包括wait()和notify()
volatile关键字 保证当前实例是线程安全
什么是同步方法?
如果一个方法中第一句话是一个同步代码块,可以将synchronized关键字定义在声明上
权限修饰符 synchronized 返回值类型 方法名(参数列表){
.....
}
锁对象:this---代表类对象的地址值引用
如果是静态的同步方法,锁对象---->当前类名.class(字节码文件对象)
死锁:
两个线程出现了一种互相等待的情况,A线程等待B线程释放锁,B线程等待A线程释放锁,造成死锁现象;
解决死锁问题--->线程之间的通信必须使用的同一个资源!(生产者和消费者模式思想)
举例:模拟生成者和消费者思想
Student类:学生数据(消费者和生成者需要使用的数据)
SetData类:生成资源类
GetData:消费者资源类
ThreadMessageTest类:实现类(创建线程)
代码体现:
实现类
public class ThreadMessageTest {
public static void main(String[] args) {
//创建一个学生对象---必须同一个
Student s = new Student() ;
//创建生产者资源类
SetData sd = new SetData(s) ;
//创建消费者资源类
GetData gd = new GetData(s) ;
//创建线程-分别操作生成者和消费者
Thread t1 = new Thread(sd) ;
Thread t2 = new Thread(gd) ;
//分别启动线程
t1.start();
t2.start();
}
}
学生类
public class Student {
String name ; //姓名
int age ; //年龄
boolean flag ; //(信号灯标记) 默认false 是否有数据
}
生成者类
public class SetData implements Runnable {
//声明学生类型的变量
private Student s ;
private int x = 0 ;//统计变量
public SetData(Student s){
this.s = s ;
}
//产生一个学数据
@Override
public void run() {
while (true) { //模拟生成者一直产生数据
synchronized (s){
//如果没有数据,等待产数据
if(s.flag){
try {
s.wait();//等待会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0){
s.name = "毛毛" ;
s.age = 22 ;
}else{
s.name = "龙龙" ;
s.age = 22 ;
}
//有数据了,唤醒
s.flag = true ;
s.notify(); //唤醒消费者线程,产数据
}
x++;
}
}
}
消费者类
public class GetData implements Runnable{
//声明学生类型的变量
private Student s ;
public GetData(Student s){
this.s = s;
}
//产生一个学生 数据
@Override
public void run() {
//不断的使用数据
while(true){
//Student s = new Student() ; //创建
synchronized (s){
if(!s.flag){
//如果有数据,等待将之前的数据消费掉
try {
s.wait();//调用 立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name+"-"+s.age);
//如果没有数据了
s.flag = false ;
//通知(唤醒)生产者线程来产数据
s.notify();
}
}
}
}
Java中的等待唤醒机制-----"信号灯法"
等待唤醒机制--->使用"信号灯法"解决线程安全前提下,造成死锁,多个线程使用同一个资源对象
使用生成者消费者模式思想:
使用生成者不断的产生数据,消费者不断的使用数据(展示数据)
当生成者等待先产生数据之后,需要通知消费者使用数据
消费者线程等待 先去使用数据,当前没有数据了,需要通知生成者产生数据!
在synchronized同步代码块/同步方法,需要使用监视器(锁)调用wait()和notify(),调用wait()会立即释放锁,完成唤醒的操作!
面试题:
为什么wait()和notify(),线程等待,线程唤醒为什么定义在Object类中?
这些方法都是和锁对象有关系,而锁对象可以是任意Java类对象,所以定义在Object类中;
调用wait(),会立即释放锁
wait()和sleep()的区别?
1)来源不同
wait()来自于Object,被锁对象调用的
sleep()来自于Thread,线程睡眠,通过线程对象调用的
2)是否会释放锁
wait()方法的调用,会立即释放锁,才能通过notify()唤醒对方线程,达到同步安全
sleep()只是Thread类的普通方法,跟锁没有关系,睡眠中,线程处于阻塞状态,当线程睡眠时间到,线程继续执行
3)共同点都会抛出异常 throws InterruptedException:中断异常
throw和throws的区别?
throws和throw共同点都是抛出的异常!
throws:
1)抛出是在方法声明上抛出
public static Date string2Date(String source,String pattern) throws ParseException,Exception{
return new SimplateDateFormat(pattern).parse(source) ;
//String dateStr ="2022-11-22" ;
//使用SimplateDateFormat里面传递的模式 "yyyy年MM月dd日",跟上面的格式不一致
}
2)throws的后面跟的异常类名,可以跟多个异常类名,中间逗号隔开
3)针对throws抛出的方法处理,谁调用这个方法,谁必须处理! (处理方式---交给调用者,继续throws或者try..catch...finally)
4)throws它表示抛出异常的可能性
throw:
1)抛出是在方法体语句中
2)后面跟是异常对象名 throw new XXXEception(),只能某个异常对象名
3)throw抛出的处理---交个方法中逻辑语句处理 if语句
4)它表示抛出异常的肯定性,执行某段代码一定会有这个异常!
11-22
1.作业:龟兔赛跑
public class Race implements Runnable{
//声明String 胜利者
private static String winner ;
@Override
public void run() {
//距离---定义0-199步
for(int x = 0 ; x <200 ; x++){//x就是步数
//如果线程名称是兔子,需要让它睡眠
if(Thread.currentThread().getName().equals("兔子") && (x%10==0)){
//睡眠给5毫秒
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了---->"+x+"步");
//调用一个方法,比赛是否结束
boolean flag = gameOver(x) ;//传入的步数
if(flag){
break;
}
}
}
//定了这个方法,必须是否结束
public boolean gameOver(int x) {//步数
if(winner!=null){
//已经有胜利者了结束
return true ;
}{//局部代码块
//判断x的步数
if(x>=199){
//给winner赋值
winner = Thread.currentThread().getName() ;
//打印胜利者
System.out.println("winer is--->"+winner);
return true ;
}
}
return false ;
}
//用户线程
public static void main(String[] args) {
//创建一个赛道
Race race = new Race() ;
//创建两个线程
//兔子和乌龟共用一个赛道
Thread t1 = new Thread(race,"兔子") ;
Thread t2 = new Thread(race,"乌龟") ;
//启动线程
t1.start() ;
t2.start() ;
}
}
2.jdk5以后Lock锁(接口)---->ReetrantLock可重入的互斥锁
Lock 这个接口
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作
实现类: 可重入的互斥锁 java.util.concurrent.locks.ReentrantLock
获取锁:指定的某个时刻 public void lock()
释放锁 : public void unlock()
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock(); //释放锁(系统相关的资源)
}
3.创建线程的方式3: 线程池(重点,面试中问的多)
1.什么是线程池
会创建一些固定的可重复使用的线程数,会在线程池中,循环利用
当某些线程使用完毕,不会被释放掉,而是归坏连接池中,等待下一次去利用!
2.线程池的好处
1.)降低资源消耗
会创建一些固定的可重复利用的线程数,会在线程池中循环利用,当某些线程使用完毕,不会被释放掉,会归坏到连接池中,等待下一次的利用
2.)提高线程维护,还是借助线程池里面的一些参数
3.线程池的操作步骤
创建线程池
//创建一个线程池
//Executors
//public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建两个线程-->放在线程池中
执行异步任务
//执行异步任务
//<T> Future<T> submit(Callable<T> task):提交异步任务,返回值就是异步任务计算的结果;
threadPool.submit(new MyCallable()); //创建异步执行任务
threadPool.submit(new MyCallable()) ;
关闭线程池
//void shutdown():关闭线程池
//关闭线程池
threadPool.shutdown();
4.线程池的优化---->参数调优(7大参数)
5.Java提供了 Timer定时器----执行定时任务TimerTask
java.util.Timer:定时器(可以执行一次或者重复执行某个任务)
构造方法:
Timer():创建一个计时器
成员方法
public void cancel()取消定时器
void schedule(TimerTask task, Date time) :在给定的日期时间来执行TimerTask定时任务 ---> String dataStr = "2022-11-22 18:00" ;//---->日期文本---->java.util.Date
(应用场景:引入使用io流的方式,在指定时间点上,删除指定带内容的目录里面的所有.java文件)
void schedule(TimerTask task, long delay) :在给定多少毫秒后(延迟时间)执行这个定时任务 public void schedule(TimerTask task,long delay,long period):在指定延迟时间(delay)执行任务,
然后每经过固定延迟时间(period)重复执行任务
schedule这些方法第一个参数都是定时任务:TimerTask是一个抽象类,
1)可以定义具体的子类继承自TimerTask
2)直接可以使用抽象类的匿名内部类
6.使用Java.io.File来描述路径形式
File(String parent, String child)
File(String pathname) 推荐第二个
基本功能:
创建文件/文件夹
public boolean createNewFile()throws IOException:创建文件,如果不存在,创建,返回true
public boolean mkdir():创建文件夹,如果存在了,则返回false;否则true
public boolean mkdirs():创建多级目录,当父目录不存在的时候创建
判断
public boolean isFile():是否是文件 使用居多
public boolean isDirectory():是否是文件夹 使用居多
public boolean isAbsolute():是否为绝对路径
public boolean exists():判断文件或者目录是否存在
删除
public boolean delete():删除由此抽象路径名表示的文件或目录 (删除目录,目录必须为空)
11-23
1.java.io.File---->高级功能:获取指定目录下的文件夹以以及文件的File数组
public File[] listFiles():获取指定抽象路径表示下的所有的File数组
推荐--->使用File的功能进行判断
public String [] list():抽象路径名表示的目录中的文件和目录
举例:
需求:获取D盘下所有的以.jpg结尾文件---->输出文件名称
public class FileTest{
pulic Static void main(String[]args){
1.描述磁盘上的抽象路径的表示d://
File file=new File("D://");
2.获取指定抽象路径表示下的所有的File数组
File [] files=file.listFiles();
3.遍历:非空判断,防止空指针异常
if(files!=null){
for(File f:files){
f---->有文件/文件夹
判断是文件
if(f.isFile()){
以.jpg结尾
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());
String getName():获取File指定的lujing下的文件或者文件的名称
}
}
}
}
}
}
public File[]listFiles(FileFilter,filter)
获取File数组的时候,就可以直接获取到指定文件的文件名称或者文件夹
参数是一个接口:文件过滤器接口
boolean accept(File pathname):
抽象路径名称所表示的路径是否放在File列表中,取决于返回值 true,否则false,不放在列表中
public File[] listFiles(FilenameFilter filter):获取File数组的时候,就可以通过文件名称过滤器按照条件进行过滤
FilenameFilter接口
boolean accept(File dir,String name):参数1:指定的目录
参数2:文件名称
返回值:true,将指定目录下的文件放在File列表中;否则false
举例:需求:获取D盘下所有的.jpg结尾文件---输出文件名称
public class FileTest2 {
public static void main(String[] args) {
//1)描述磁盘上抽象路径的表示d://
File file = new File("D://") ;
//public File[] listFiles(FileFilter filter) 获取File数组的时候,就可以直接获取到指定条件的文件名称或者文件夹
/* File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//抽象路径名称所表示的路径是否放在File列表中,取决于返回值 true,将这个路径放在列表中,
// 否则false,不放在列表中
//获取D盘下所有的以.jpg结尾文件--->输出文件名称
// System.out.println(pathname);
return (pathname.isFile() && pathname.getName().endsWith(".jpg"));
}
});*/
//public File[] listFiles(FilenameFilter filter):获取File数组的时候,就可以通过文件名称过滤器按照条件进行过滤
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//创建File对象-->判断File是否为文件
File f = new File(dir,name) ;//文件放在指定目录中
//判断f的路径---->指定的文件 并且name文件名称必须以.jpg结尾
boolean flag1 = f.isFile();
boolean flag2 = (name.endsWith(".jpg")) ;
return flag1 && flag2 ;
}
});
//遍历File数组
if(files!=null){
for(File f:files){
System.out.println(f.getName());
}
}
}
}
2.递归算法
递归:方法调用本身的一种现象! 不是方法嵌套方法
java.lang.Math.max(10,Math.max(20,15));
伪代码
public void show(int n){
while(n<0){
break;
}
System.out.println(n);
show(n--);
}
方法递归:
1.)需要有方法
2.)有一定的规律
3.有方法结束的条件(出口条件),否则"死递归"
构造方法没有递归
不死神兔问题
题目:有一对兔子,从初始后第三个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,加入兔子不死,第二十个月的兔子对数为多少?
规律
第一个月:
1对
第二个月:
1对
第三个月:
2对
第四个月:
3对
第五个月:
5对
第六个月:
8对
代码体现
public class Test1 {
public static void main(String[] args) {
方式1:数组
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
for (int x = 2; x < arr.length; x++) {
arr[x] = arr[x - 1] + arr[x - 2];
System.out.println("兔子的对数是:" + arr[x]);
}
System.out.println("第二十个月的兔子的对数是:"+show(20));
}
方式2:递归方法
public static int show(int n){
if (n==1||n==2){
return 1;
}else {
return show(n-1)+show(n-2);
}
}
}
3.IO流的分类(输入流和输出流)
1.)按流的方向划分
输入流----->读
输出流----->写
2.)按流的类型划分---同时按方向划分
字节流
字节输入流:InputStream--->不能实例化--->具体的子类:针对文件的字节输入流 FileInputStream;
字节输出流:OutputStream--->不能实例化--->具体的子类:针对文件的字节输出流FileOutputStream
字节缓冲流(字节高效流)
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOutputStream
字符流
字符输入流
字符输出流
windows系统下通过io流的方式(字节流) 写换行--->"\r\n" 换行符号
创建字节输出流对象,实现文件的末尾追加,而不将之前覆盖,第二个参数必须为true
public FileOutputStream(String name,boolean append) throws FilenotFoundException
IO流操作的时候,加入异常处理代码的格式--->开发中 try....catch...finally
字节输入流:InputStream--->读 抽象类
提供子类:FileInputStream:针对文件操作的字节输入流
1.)创建文件字节输入流对象
2.)读文件
public int read() throws IOException:一次读取一个字节,返回字节数---->速度较慢
public int read(byte [] b) throws IOException:一次读取一个字节数组---->速度较快
3.)释放资源
文件字节输入流一次读取一个字节,将文件内容输出控制台撒谎上,中文出现乱码,因为
字节流读取字节的时候---将字节--->强转成char,只考虑英文(abcd)--->97---(char)97,当英文的后面有中文拼接
无法解析强转了--->乱码--->Java才提供了字符流
什么时候使用字符流,当使用记事本打开能读懂的就使用字符;打开读不懂,用字节(读图片文件/视频/音频)
4.throw和throws的区别?
1.)抛出的位置不同
throws抛出在方法声明上
throw抛出在方法体上
2.后面使用异常格式不同
throws 后面跟的异常类名,而且中间逗号隔开,可以抛出多个异常
throw后面跟的异常对象 呢哇XXXException();跟的具体的某一个异常对象
3.)处理异常方式不同
针对带有throws的方法,异常处理是交给调用者处理!
针对throw抛出异常的处理,交给方法体中逻辑语句处理!
举例:
if(条件表达式){
throw new XXXException()
}
4.)是否抛出的异常的肯定性
throws:表示抛出异常的可能性,执行某段代码,可能出现问题
String的日期文本--->java.util.Data格式--->解析parse(通过SimpleDateFormat)
"2022-11-26"
throw:表示抛出异常的肯定性,执行方法体中某段代码,一定会出现异常!