多线程_IO流_网络编程引入

一、多线程的创建方式

1.多线程的创建方式1 ----继承Thread类

1)自定义一个类继承自Thread,"线程类"
2)重写Thread类的run方法
3)在main用户线程中,创建当前类对象,
4)start()启动线程----->jvm调用run方法,结果---多个线程并发执行!
class MyThread extends Thread{
    
    public void run(){
        //完成耗时的操作
    }
}

public class TestTread{
    public static void main(String[] args){
        //创建线程类对象
		MyThread my1=  new MyThread() ;
        MyThread my2=  new MyThread() ;
        ///设置线程名称
        my1.setName("t1") ;
        my2.setName("t2") ;
        //启动线程
        my1.start() ;
        my2.start();
    }
}

2.多线程的创建方式2:---要实现Runnable接口

1)自定义一个类实现Runnable接口,
2)实现接口里面的run方法--->完成耗时操作
3)在main用户线程中创建当前这个类的实例--->"资源类对象"
4)创建线程类Thread对象,然后将3)资源类对象 作为参数传递.
5)启动线程!start();
   
	class MyRunnable implements Runnable{}//实现接口
	MyRunnable my = new MyRunnable() ; ---->真实角色
	Thread  t1  = new Thread(my,"t1") ; ---->Thread代理角色
	t1.start();

3.创建线程的方式3 :线程池

1)自定义一个类实现Callable接口,实现里面call方法,完成异步任务的操作
    2)创建线程池 Executors.newFixedThreadPool(线程数) ;---->ExecutorService
    					submit(Callable call)
    					submit(Runnable run)

	public class ExecutorsDemo {
	    public static void main(String[] args) {
		// 创建一个线程池对象,控制要创建几个线程对象。
		// public static ExecutorService newFixedThreadPool(int nThreads)
		ExecutorService pool = Executors.newFixedThreadPool(2);
		// 可以执行Runnable对象或者Callable对象代表的线程
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable()); 
		pool.shutdown();//结束线程池
	    }
    }

二、Java设计模式 结构型设计-->代理模式--静态代理

代理设计模式
代理核心思想:
     真实角色专注自己的事情(开发中,针对自己的业务)
     代理角色帮助真实完成一件事情
    静态代理:
        代理角色和真实角色要实现同一个接口
    动态代理:
        jdk动态代理-―-前提必须有接口
        cglib动态代理--->基于类就可以

三、解决线程安全问题

校验多线程安全问题的标准 (使用标准来看多线程环境是否存在问题,以及解决方案)
    1)是否是多线程环境   --->是
    2)是否有共享数据    ---> 是存在的
    3)是否有多条语句对共享数据操作
  		  tickets票:多条语句同时操作
    将3)多条语句多共享数据的操作使用同步代码块包起来---解决线程安全问题	
synchronized(锁对象){
	将多条语句多共享数据的包裹起来
}
锁对象:可以是任意Java类对象(多个线程必须使用同一个锁!否则锁不住)
可能出现死锁死锁:
	使用synchronized解决线程安全问题,安全问题解决了,效率低,可能死锁现象
	死锁:两个线程出现了一种互相等待的情况,A线程等待B线程释放锁,B线程等待A线程释放锁,造成死锁现象!
	解决死锁问题--->线程之间的通信必须使用的同一个资源!  (生产者和消费者模式思想)

消费者生产者模式思想------"信号灯法"

四、什么是同步机制

1.同步机制

多个线程在并发的使用公共的某个变量(被共用),,保证同步(保证安全),某个线程在当前这个数据进行持有的时候,其他线程是不能访问的----通过synchronized同步代码块就可以解决线程安全问题
		synchronized(监视器){
		
		}
	1)需要将多条语句对共享数据进行操作使用同步代码块包裹起来
	2)同步机制里面的监视器,俗称"锁",可以是任意Java类对象,多个线程必须使用同一个监视器
		或者使用同步方法(非静态)---锁对象this
		静态的同步方法--->锁对象当前类名.class字节码文件对象


同步机制:
	包括wait()和notify()
		这两个方法都是监视器(锁)有关系,锁对象可以是任意Java类型(jdk提供 任何类,自定义的类),Object代表所有类的父类,任意Java对象可以使用Object,所以这些都定义在Object类中.
	volatile关键字  保证当前实例是线程安全

2.wait()和sleep()的区别?

1)来源不同
	wait()来自于Object,被锁对象调用的
	sleep()来自于Thread,线程睡眠,通过线程对象调用的
2)是否会释放锁
	wait()方法的调用,会立即释放锁,才能通过notify()唤醒对方线程,达到同步安全
	sleep()只是Thread类的普通方法,跟锁没有关系,睡眠中,线程处于阻塞状态,当线程睡眠时间到,线程继续执行
3)共同点都会抛出异常 throws InterruptedException:中断异常

五、等待唤醒机制

等待唤醒机制--->使用"信号灯法"解决线程安全前提下,造成死锁,多个线程使用同一个资源对象
使用生成者消费者模式思想:
    使用生成者不断的产生数据,消费者不断的使用数据(展示数据)
    当生成者等待先产生数据之后,需要通知消费者使用数据
    消费者线程等待 先去使用数据,当前没有数据了,需要通知生成者产生数据!
    
    在synchronized同步代码块/同步方法,需要使用监视器(锁)调用wait()和notify(),调用wait()会立即释放锁,完成唤醒的操作!

六、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)它表示抛出异常的肯定性,执行某段代码一定会有这个异常!

七、jdk5以后Lock锁---->ReetrantLock可重入的互斥锁

Lock接口
	LockLock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作.
实现类:
	可重入的互斥锁   java.util.concurrent.locks.ReentrantLock
	获取锁:指定的某个时刻   public void lock()
	释放锁 : public void unlock()	
格式:
	Lock l = ...;
      l.lock();
      	try {
        	//访问受此锁保护的资源
 		} finally {
                l.unlock();    //释放锁(系统相关的资源)
       }
步骤:
	1)//可重入的互斥锁
	private Lock lock = new ReentrantLock() ;
	2)//获取锁
	lock.lock();
	3)//释放锁
	lock.unlock();

八、线程池

1.什么是线程池

什么是线程池:
    会创建一些固定的可重复使用的线程数,会在线程池中,循环利用.当某些线程使用完毕,不会被释放掉,而是归还线程池中,等待下一次再去利用!
线程池的特点:
    1)降低资源销毁,因为线程使用完毕的时候,归还到线程中继续使用!
    2)提高线程的维护,还是借助线程池的参数

2.线程池的优化  参数调优(7大参数)

corePoolSize:核心线程数量
	maxmumPoolSize:最大核心线程数
	workQueue:阻塞队列
	keepAliveTime:生存时间
	TimeUnit unit:时间计量单位
	handler:拒绝策略:当线程数已经到最大线核心线程池并且同时workeQueue里面队列到达满的状态,线程池会启用拒绝策略!			---->里面也是子实现类---->都是Exceutors的静态内部类
	
	ThreadFactory: 创建线程的工厂------>创建默认的线程池的名称以及后面的序列编号/同时创建线程
					----->子实现类DefaultThreadFactory--->Exceutors的静态内部类
					pool-1-Thread-1
					pool-1-Thread-2
					....

九、文件的基本操作

1.文件的基本功能

使用java.io.File来描述路径形式
	描述 "D:\EE_2211\day23\code\Employee.java"
	File(File parent, String child)
	File(String pathname) 推荐第二个
	File(String parent, String child)
	
创建文件/文件夹
	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():删除由此抽象路径名表示的文件或目录 (删除目录,目录必须为空)

2.文件高级功能

获取指定目录下的所有的文件夹以及文件的Fiie数组
例题:获取E/D盘下的所有的以.jpg结尾的文件
java.io.File:高级功能
	//public File[] listFiles():获取指定抽象路径表示下的所有的File数组  推荐---->使用File的功能进行判断
	//public String[] list():抽象路径名表示的目录中的文件和目录。
    
    public class FileTest {
    public static void main(String[] args) {

        //1)描述磁盘上抽象路径的表示d://
        File file = new File("D://") ;
        //public String[] list():抽象路径名表示的目录中的文件和目录。
       /* String[] strs = file.list();
        for(String s:strs){
            System.out.println(s) ;
        }*/
        //public File[] listFiles():获取指定抽象路径表示下的所有的File数组  推荐---->使用File的功能进行判断
        File[] files = file.listFiles();
        //遍历之前:非空判断,防止空指针异常
        if(files!=null){
            for(File f :files){
                //f---->有文件/文件夹
                //判断是文件
                if(f.isFile()){
                    //以.jpg结尾
                    if(f.getName().endsWith(".jpg")){//.endsWith以...结尾
                        System.out.println(f.getName());//String getName():获取File指定的路径的文件或者文件的名称
                    }
                }
            }
        }
    }
}

十、IO流

1.流的分类

按流的方向划分两大类:
		输入流
		输出流
按类型划分:
		字节流
			字节输入流:InputStream
						基本的字节输入流:FileInputStream
						
                        字节缓冲输入流:BufferedInputStream 
			字节输出流:OutputStream
						基本的字节输出流:FileOutputStream
						字节缓冲输出流BufferedOutputStream
		字符流
			字符输入流:Reader
							FileReader 
						字符缓冲输入流:BufferedReader
			字符输出流:writer
							FileWriter
						字符缓冲输出流	:BufferedWriter	
当使用记事本打开能读懂的就使用字符;打开读不懂,用字节!(读图片文件/视频/音频)

2.基本功能

2.1 基本功能

//public FileOutputStream(String name,boolean append) throws FileNotFoundException
//创建字节文件输出流对象,实现文件的末尾追加,而不将之前覆盖,第二个参数必须为true
        FileOutputStream fos = new FileOutputStream("fos2.txt",true) ;
        //for循环
        for(int x = 0 ; x < 10;x++){
            fos.write(("hello"+x).getBytes());//getBytes()获取字节
            //写一次数据,换一次行
            fos.write("\r\n".getBytes());
        }
        //释放资源
        fos.close();

2.2 输入流读文件

步骤:
	1)创建文件输入流对象
	2)读文件
		public int read() throws IOException:一次读取一个字节,返回字节数
		public int read(byte[] b) throws IOException:一次读取一个字节数组
	3)释放资源
实现过程:
方式一:使用基本字节流一次读取一个字节:
    //1)创建文件字节输入流对象
     FileInputStream fis = new FileInputStream("IODemo3.java") ;
	//2)读文件
	//有一个字节数:从0开始
     int len = 0 ;
     while((len=fis.read())!=-1){ //read()阻塞式方法  //len赋值给流对象调用的读的方法,判断一块在这里用
     	System.out.print((char)len) ;//将数据展示控制台上
     }

	//3)释放资源
    fis.close();
方式二:使用基本字节流一次读取一个字节数组:
	//创建一个字节文件输入流对象
    FileInputStream fis = new FileInputStream("IODemo3.java" ) ;
	//一次读取一个字节数组,字节数组长度,1024或者1024的整数倍
    byte[] bytes = new byte[1024] ;
    //总字节数
    int len = 0 ;
    while((len=fis.read(bytes))!=-1){ //赋值,判断一块使用
    	//将数据展示控制台上
    	System.out.println(new String(bytes,0,len));
    }
	//释放资源
	fis.close() ;

2.3 复制文件

方式一:使用基本字节流一次读取一个字节:
		//使用文件字节输入流操作源文件
        FileInputStream fis = new FileInputStream(srcFile) ;
        //使用文件字节输出流操作目的地文件
        FileOutputStream fos = new FileOutputStream(destFile) ;

        //一次读取一个字节
        int len = 0 ;
        while((len=fis.read())!=-1){
            //读一个字节,给fos流对象中写一个字节,写入到目标文件中
            fos.write(len) ;
        }

        //释放资源
        fos.close();
        fis.close();
方式二:使用基本字节流一次读取一个字节数组:
		//使用文件字节输入流操作源文件
        FileInputStream fis = new FileInputStream(srcFile) ;
        //使用文件字节输出流操作目的地文件
        FileOutputStream fos = new FileOutputStream(destFile) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        //实际字节数
        int len = 0 ;
        while ((len=fis.read(bytes))!=-1){
            //一次读取一个字节数组从字节输入流中,
            //一次写一个字节数组,通过fos写入到destFile中
            fos.write(bytes,0,len);
        }

        //释放
        fos.close();
        fis.close();

2.4 Buffered

Bufferered键盘录入

InputStream in = System.in ; //标准输入流---等待录入
        //创建字符输入流:Reader抽象类
        //InputStreamReader是具体的子类 构造方法InputStreamReader(InputStream in )
       	Reader reader = new InputStreamReader(in) ;
        //创建一个字符缓冲输入流对象
        BufferedReader br = new BufferedReader(reader) ;

		//提示并录入
        System.out.println("请您输入一个字符串:");
        //利用BufferedReader一次读取一行
        String line = br.readLine() ;
        System.out.println("您录入的数据是:"+line) ;

2.5 字节缓冲流复制文件

字节缓冲流的方式
		使用字节缓冲流的方式一次读取一个字节  
		使用字节缓冲流的方式一次读取一个字节数组  
        
        public class CopyFileTest {
    public static void main(String[] args) throws IOException {
        //D:\EE_2211\day24\a.mp4---复制到 当前项目copy.mp4
        long start = System.currentTimeMillis() ;
       // copyFile("D:\\EE_2211\\day24\\a.mp4","copy.mp4") ;
        copyFile2("D:\\EE_2211\\day24\\a.mp4","copy.mp4") ;
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时:"+(end-start)+"毫秒");
    }

    //字节缓冲流一次读取一个字节数组
    public static void copyFile2(String srcFile, String destFile) throws IOException {
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream(srcFile)) ;
        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile)) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);

        }
        bos.flush() ;//强制刷新缓冲字节  (网络中 TCP传输协议,这种场景刷新,客户端文件---传给服务器端)

        //释放资源
        bos.close();
        bis.close();
    }

    //字节缓冲流一次读取一个字节
    public static void copyFile(String srcFile, String destFile) throws IOException {
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream(srcFile)) ;
        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile)) ;

        //一次读取一个字节
        int by = 0 ;
        while((by=bis.read())!=-1){
            //写一个字节
            bos.write(by) ;
        }
        //释放资源
        bos.close();
        bis.close();
    }
}

3.字符流

3.1 Writer

Writer:具体的子类
	public OutputStreamWriter(OutputStream out):
	字符转换输出流(可以将基本字节输出流转换成字符输出流),平台默认的字符集编码(idea,utf-8)
	public OutputStreamWriter(OutputStream out,String charsetName):
	字符转换输出流 ,指定一个编码字符集
写的功能:
	void write(char[] cbuf)写入一个字符数组。
	abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
	void write(int c)  写一个字符
	void write(String str)写一个字符串
	void write(String str, int off, int len) :写入字符串的一部分

3.2 Reader

Reader:抽象类,具体子类
	public InputStreamReader(InputStream in):创建字符转换输入流,以平台默认字符集解码
	public InputStreamReader(InputStream in,String  charsetName):创建字符转换输入流对象,指定字符集解码
read的功能
	public int read(char[] cbuf):读取字符数组
	public int read():读一个字符
	InputStreamReader/OutputStreamWriter:字符转换流弊端:代码格式复杂,不能直接操作文件!

4.序列化和反序列化作用

序列化:前提条件:当前这类型必须实现java.io.serializable接口
    将Java对象(成员信息等) 变成  "流数据",在服务器之间数据传输
反序列化:
        即将序列化流中的数据---->ObjectInputStream---->还原成对象!
        
大部分的常用类都已经实现了java.io.serializable接口,后期Java后端程序和前端的中间连接层---->底层框架
Servlet(Server Applet)---->里面给产生固定的serialVersionUID

十一、java.util.Properties(很重要):属性列表集合类

Properties类表示一组持久的属性。
Properties可以保存到流中或从流中加载。(重要的地方)
属性列表中的每个键及其对应的值都是一个字符串。

继承Hashtable---->实现Map接口---->存储数据,取出数据----都可以使用Map的方式

特有方法:

java.util.Properites属性列表有自己的遍历方式---底层基于Map实现的
       添加元素:
               public Object setProperty(String key, String value)
       遍历属性列表
               public Set<String> stringPropertyNames()获取属性列表中的所有的键
               public String getProperty(String key)使用此属性列表中指定的键搜索属性

十二、网络编程

数据在网络中不断进行传输
网络编程三要素:
ip: 使用点分十进制法,中间使用.隔开
	A类  国家大部门---->前一个号段是网络号段,后面三个主机号段
	B类  校园/大公司服务器机房/:前面两个网络号段,后面使用两个注解号段
	C类  私人地址:前面三个为网络号段,后面是主机号段
port端口
	port端口 360软件都可以查看你电脑所有客户端软件的端口号
	范围:0~65535  里面0~1024属于保留端口
传输协议
	UDP协议--->数据报包(数据包的报文)
		1)不需要建立连接通道
		2)不可靠协议,不安全的
		3)发送数据大小有限制
	TCP协议--->最基本的字节流的方式发送数据
		1)就必须连接通道
		2)可靠协议,一种安全
		3)发送数据大小无限制

UDP发送端和接收端的实现(了解) --->不可靠连接

UDP发送端的代码实现:
//1)创建发送端的socket
        DatagramSocket ds = new DatagramSocket() ;
        // 2)创建数据报包DatagramPacket       
         /** public DatagramPacket(byte[] buf,  要发送数据---转换成字节数组
         *                       int length,  实际字节数长度
         *                       InetAddress address,  ip地址对象
         *                       int port)  端口号
         */
        byte[] bytes = "hello,UDP我来了".getBytes() ;
        int length = bytes.length ;
        InetAddress inetAddress = InetAddress.getByName("192.168.1.5");//无线ip不断变的--->本地回环地址127.0.0.1
        int port = 10086 ;
        DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port);

        //3)使用发送端的Socket将数据存储数据包中, 发送(本质存储数据包)       
        ds.send(dp) ;
        //4)释放资源
        ds.close() ;

UDP接收端的代码实现:
//1)创建接收端的Socket对象,绑定端口       
        DatagramSocket ds = new DatagramSocket(10086) ;

        //2)创建一个接收容器--->数据包--->自定义字节缓冲区,将发送的数据包
        byte[] bytes = new byte[1024] ;//1024或者1024整数倍
        int length = bytes.length ;
        DatagramPacket dp = new DatagramPacket(bytes,length) ; //将发送端数据缓冲到这个接收容器中

        //3)接收,以上面这个接收容器来接收
        ds.receive(dp);

        //4)从接收容器中解析数据包的实际内容数据
        //从接收容器中获取public byte[] getData() 实际缓冲区的对象(从上bytes分段取数据)
        byte[] bytes2 = dp.getData();
        //获取里面实际缓冲区的长度
       // public int getLength()
        int length2 = dp.getLength();
        //展示数据---分段取数据,每次从0开始取实际长度
        String msg = new String(bytes2,0,length2) ;
        //数据包里面获取哪一个ip地址发来的--->ip地址字符串形式
        String ip = dp.getAddress().getHostAddress() ;
        System.out.println("data from --->"+ip+",发送内容是:"+msg);

        //释放资源
        ds.close();

十三、每周一个算法

1.递归算法

方法调用方法本身的一种现象!(并非是方法嵌套方法)
递归的使用:
	1)需要定义一个方法
	2)有规律
	3)必须有出口条件----->"死递归"
	
需求:求5的阶乘
   使用求阶乘思想完成
   使用递归思想
        解决问题的思路:
               将问题1--->进行分解若干小问题
                      问题11
                      问题12
                           ...
public class DiGuiDemo {

   public static void main(String[] args) {

       //使用求阶乘思想完成
       //定义一个结果变量
       int jc = 1 ;
       for(int x = 2 ;x<=5 ; x ++){
           jc*=x ;
       }
       System.out.println("5的阶乘是:"+jc);

       System.out.println("-------------------------------------") ;
       System.out.println("5的阶乘是:"+jieCheng(5));
   }

    //定义求5的阶乘的方法
    private static int jieCheng(int n) {//5
       if(n==1){
           return  1 ;
       }else {
           //如果不是1
           //5*调用方法名(5-1)
           return n * jieCheng(n-1) ;
           //5 *4*3*2*jiecheng(1)
       }
    }
}

2.不死神兔问题

/*
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?
*/
public class Test {
    public static void main(String[] args) {
        //数组的方式去实现
        //创建一个数组:动态初始化
        int[] arr = new int[20] ; //第二十个月
        //第一个月和第二个都是1
        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[19]); //6765
        System.out.println("-----------------------------------------------------") ;

        //递归:定义一个方法
        long time = System.currentTimeMillis() ;
        System.out.println("第二十个月的兔子的对数是:"+getRabbit(20));
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end-time)+"毫秒");
    }
    //定义一个方法
    public static int getRabbit(int n){//代表的第几个月
        //第一个月和第二个月兔子的对数是1---->方法递归的出口条件
        if(n==1 || n==2){
            return  1;
        }else {
            //从第三个月开始,每一个月兔子对数是前两个月兔子对数之和!
            return getRabbit(n-1)+getRabbit(n-2) ;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值