1.多线程
概念:
1.并发和并行:
- 并行: 两个任务真的是同时执行的
- 并发: 两个任务是交替执行的
- 总结:java是基于并发
2.进程和线程:
- 进程: 其实就是系统为程序匹配的内存空间,有一个编号,这个编号称为进程id,并没有指定代码的能力
- 线程: 是进程中的工作单元,是用来执行代码的
- 结论:我们的代码都是线程执行的
创建方式:
第一种:
- 定义一个类去继承Thread的类
- 重写run方法
- 将你要执行的代码放到run方法中
- 创建线程的子类对象
- 调用对象的start方法进行线程的开启
第二种:
- 定义一个类实现Runnable接口
- 重写run方法
- 将你要执行的代码方法run方法中
- 创建Runnable的实现类对象
- 创建Thread对象,将Runnable的实现类传递进来
- 调用Thread的start开启线程
两者对比:
1.继承方式:
- 优点:可以直接使用Thread中的功能
- 缺点:因为java是单继承,这个类不能在继承其他的类了,比较死板,代码和线程绑定比较紧密,不灵活
2.实现方式:
- 优点:一个类实现接口的时候,还可以继承其他的类,比较灵活,可以将任何和线程分离
- 缺点:他不能直接使用Thread中的功能,但是可以弥补,用static Thread currentThread(); 返回正在执行代码的当前线程
交替执行的原因:
- 因为每开启一条线程,就会出现一个独立的栈空间,cup切换就是栈空间的代码,所以多个栈空间的代码出现了交替执行的现象,根本原因是cpu随机切换导致的
注意事项:
- 线程从哪里跌倒,就从哪里爬起来
- start方法才是真正开启线程的方法,因为底层调用了native的本地方法start0,run只是一个普通的方法,我们调用start方法的时候,底层会自动帮我们调用run方法
1.2 Thread
构造:
- Thread(Runnable r); 创建线程并设置任务
- Thread(Rannanle r, String name); 创建线程并设置任务,给线程起名字
功能:
- void start(); 开启线程,执行线程任务!
- setName(String name); 设置线程名字
- String getName(); 获取名字
- static Thread currentThread(); 返回正在执行代码的当前线程
- static Thread sleep(long time); 让当前线程处于睡眠状态,单位时间为毫秒
- void setPriority(int value ) 设置线程的优先级,[1,10], 其中默认值为5
- setDaemon(boolean true) 一定要开启线程之前的设置,让当前线程作为守护线程,守护其他所有非守护线程
多线程存在的问题:当多个线程操作共享数据的时候,会出现线程安全问题
解决方案:
1.通过同步代码块解决
synchronized(锁对象){
这里放置 ---> 需要被一条线程执行完其他线程才能进来的代码
}
注意事项: 锁对象可以是任何引用数据类型, 但是一定要保证锁对象唯一, 否则出现从后面进的情况!!!!!!!!!
2.通过同步方法解决(不推荐!!!!!!)
缺点:
1.锁对象是固定死的,普通的成员方法是this,静态的成员方法时当前类的字节码对象
2.要锁就将整个方法的代码都锁起来了!!!!!无法锁定部分代码!!!!!
同步方法的类都被淘汰了
Vector ArrayList集合的前身
Hashtable HashMap
3.注意点:
- ArrayList , HashMap, StringBuilder都是线程不安全!!!!!!
- Collections.synchronizedXxx的方法可以将不安全的集合变成安全的集合,就是给原有集合的所有的方法加上了同步代码块!!!!
- StringBuffer线程安全, 和StringBuilder用法完全一样!!!!!!!!
3.jdk1.5的Lock锁机制
Lock lock = new ReentrantLock();
lock();和unlock();
缺点:
1.需要自己维护锁,必须保证锁对象唯一。
2. unlock最好放到finally里面, 如果不放在这里一旦出了异常, 没有释放锁, 这个空间就锁死了!!!!!
4.线程间通信
5.线程的状态
- NEW 创建线程对象, 但是还未调用start方法
- RUNNABLE 线程对象已经调用完了start方法, 具备被cpu扫描到的资格的状态
- BLOCKED 线程正在被cpu执行, 但是遇到锁, 进不去的状态!!!!!
- WAITING 线程遇到wait方法, 处于无限等待, 只能被notify或者notifyAll唤醒
- TIMED_WAITING 线程遇到了sleep(long time), wait(long time), 处于计时等待
- TERMINATED 线程任务执行完后, 线程消亡的状态
6.线程池
作用: 解决我们频繁创建线程以及销毁线程的过程, 节约了资源以及提高的程序的运行效率, 创建线程比较耗费时间的.
第一种:
- 造出的池子默认没有线程, 我们提交任务, 如果没有线程, 他会自动帮我们创建线程, 然后执行任务, 我们省力, 我们不用再关系创建线程的动作, 被线程池管理了
- Executors 的 static ExecutorService newCachedThreadPool()
- 总结: 后期用的比较少, 因为线程的数量没有限制, 可能会造成后期线程数据过多, 导致cpu压力过高!!!!!
第二种:
- static ExecutorService newFixedThreadPool(int nThreads)
- 刚开里面也是空的, 但是他设置上限, 缺点: 不能够控制闲时和忙时的线程数量, 以及没有拒绝策略
第三种(一般写5个参数就可以,剩余两个默认):
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- 参数一 核心数量
- 参数二 最大数量
- 参数三 临时的销毁的默认时间
- 参数四 时间的单位 TimeUtil枚举类有各种时间单位
- 参数五 任务队列 LinkedBlockingQueue 链表阻塞队列 ArrayBlockingQueue集合阻塞队列
- 参数六 Excutors.defaultThreadFactory()
- 参数七 拒绝策略
- new ThreadPoolExecutor.AbortPolicy()多余的任务丢弃掉, 并抛出异常
- new ThreadPoolExecutor.DiscardPolicy()多余的任务丢弃掉, 不抛出异常
- new ThreadPoolExecutor.DiscardOldestPolicy()多余的任务将等待最久丢弃掉
- new ThreadPoolExecutor.CallerRunsPolicy()如果有多余不抛弃, 让其他线程来执行!!!!!
功能:
- submit(Runnable r) 向线程池中提交任务, 并且执行
- shutdown() 关闭线程池,以后绝对不会用, 项目下线了, 服务器停止了!!!!!!(一般不手动关闭)
2.网络编程
作用: 实现网络中设备和设备之间数据的收发
概念:ip、端口和协议
ip:
- IPV4
- 由32个二进制组成, 最多40个亿
- 192.168.11.225
- 公网ip 一级IP(42个亿公网IP)
- 只有公网IP才能真正实现网络数据传输!!!!!!, 只有公网IP在公用网络中才能找到他
- 小命令 ipconfig 显示本机的ip相关的信息 127.0.0.1 代表本机地址!!!!!!
- InetAddress static InetAddress getByName(ip地址的字符串| 主机名);建议大家传入IP地址, 因为主机名不唯一
- IPV6
- 由128个二进制组成, 但是现在ipv4还是主流
端口:
本质: 是程序在设备中的编号, 他的范围是[0, 65535], 0-1024之间不建议大家使用否则会先端口被占用的风险
协议:
1.UDP:
面向无连接, 速度快, 可能会丢失数据
- DatagramSocket
- 构造
- DatagramSocket()创建UDP的程序, 但是端口随机
- DatagramSocket(int port)创建UDP的程序, 指定端口
- 功能
- send(DatagramPacket dp);发送数据
- DatagramPacket
- DatagramPacket(byte[] arr, int length);用于接受数据的包
- DatagramPacket(byte[] arr, int length,InetAddress address,int port);用于发送数据的包
- receive(DatagramPacket dp);接受数据
- void close();关闭资源, 底层使用的是io流技术实现的
- send(DatagramPacket dp);发送数据
2.TCP:
面向连接, 速度稍慢, 不会丢失数据
客户端Socket
用来创建客户端的TCP
- 构造
- Socket(String ip, int port) 创建Socket链接指定ip, 指定端口的服务器
- 功能
- InputStream getInputStream(); 获取听筒
- OutputStream getOutputStream(); 获取话筒。
- void close(); 关闭资源
- shutdownOutput(); 关闭话筒
服务器ServerSocket
- 构造
- ServerSocket(int port) 启动服务器程序, 并且设置端口
- 功能
- Socket accept(); 监听客户端链接, 一旦有链接则创建Socket为其服务
- 注意:
- 1. ServerSocket的accept方法是阻塞方法, 如果没有客户端链接, 则一直处于等待
- 2. 网络流所有的read方法都是读的方法都是阻塞方法, 如果结束不到结束标记或者数据就会一直等待
3.扩展
3.1 PrintStream
概念: 字节打印流, 本质就是一条输出流, 不过提供了特殊的方法
- 构造
- PrintStream(String fileName) 创建打印流, 指向指定文件
- PrintStream(OutputStream out) 创建打印流, 指向其他输出流的目的地
- 功能
- write相关的方法 用来写字节数据
- println相关的方法 用来写基本类型以及字符串数据, 而且自动写换行符!!!!!!
- close 关闭资源.
- 特点:既可以写字节数据, 也可以写字符数据, 经常用于即时通信技术!!!!!其他地方比较少见
3.2 UUID
作用: 产生36位不重复的字符串
- 功能
- UUID randomUUID();
- String toString(); 得到随机的字符串
3.3位运算
- >>
- 将整数变成二进制, 右移几位, 前面补0
- &
- 遇0则0,一个数 & 15 结果的范围是4个二进制 , 本质 就是[0,15]之间
- ^
- 相同为0, 不同为1
- 文件夹小算法
- 作用: 一般用于大量文件存储的时候, 为了提高文件的访问速度, 需要将文件存放到多级文件夹中
4.log日志
logback的使用
- 1.将jar包拷贝到当前模块下的lib目录中
- 2.右键 ----> add as lib......展开jar包!!!!!!到当前模块的环境中
- 3.将配置文件放到 src下面
- 4.创建核心对象打印日志
- private static final Logger LOGGER = LoggerFactory.getLogger(Test1.class);
- 5.使用他的功能来输出日志 debug info error xxxx
日志的级别大小:
all trace debug info warn errer fatal off
好处:
- 1.可以通过配置文件控制日志的输出,不需要改动原码!!!!!!
- 2.可以将日志持久化存储
注意:只会输出 >= 配置的级别的日志!!!!!!
5.单例设计模式
作用: 保证一个类有且只能创建一个对象
使用场景: 可以用来共享数据
实现方式:
方式1 : 程序员式
- 1.私有化构造
2.维护一个public final static 本类对象
3.通过类名.对象名
public class Person {
//私有化构造函数!!!!!
private Person(){}
//必须在本类中想办法调用构造造一个对象
public final static Person person = new Person();
}
方式2: 饿汉式
- 1.私有化构造
2.维护一个private static 本类对象
3.提供一个public 静态的方法返回本类对象
4. 类名.静态方法获取本类对象
public class Person1 {
//私有化构造函数!!!!!
private Person1(){}
//必须在本类中想办法调用构造造一个对象
private static Person1 person = new Person1();//提供一个公共静态的方法返回该对象
public static Person1 getInstance(){
return person;
}
}
方式3: 懒汉式(延时加载)
- 1.私有化构造
2.维护一个private static 本类引用
3.提供一个public 静态的方法返回本类对象,方法体中判断对象是为null, 如果为null创建对象
4. 类名.静态方法获取本类对象
public class Person2 {
//私有化构造函数!!!!!
private Person2() {
}//必须在本类中想办法调用构造造一个引用
private static Person2 person;//提供一个公共静态的方法返回该对象
public synchronized static Person2 getInstance() {
if (person == null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
person = new Person2();
}
return person;
}
}
问题:多线程访问可能出现线程安全问题
解决:给方法加锁
6.类加载器
作用:
1.帮我们将硬盘上的字节码文件加载到内存中,jvm自动帮我们做的, 我们只需要理解
2.可以读取字节码路径下的东西(掌握, 需要能够加载字节码路径自己的文件!!!!!)
步骤:
- 1.获取类加载器
- ClassLoader cl = 当前类.class.getClassLoader();
- 2.使用类加载的功能
- 1. InputStream getResourceAsStream(String path); 以流的方式,关联字节码路径下的 "path"所对应的文件
- 2. URL getResource(String path); 以URL的方式,关联字节码路径下的 "path"所对应的文件
- URL 统一资源定位符,内置动态获取的绝对路径
- String getPath(); 得到绝对路径
- URL 统一资源定位符,内置动态获取的绝对路径
类加载的分类:
- bootstrap classloader(启动类加载器)
- 平台类加载器, 加载一些底层的非常核心的东西, 不是java语言写的, 是c++语言写的, java强行获取这个类加载的话, 会得到null, 加载java以外的东西!!!!!
- platform classloader(平台类加载器)
- 加载大部分的jdk的扩展包的字节码文件, 以及少量特殊的JDK的类
- system classloader(系统类加载器)
- 负责加载大部分的jdk的类,以及程序员自己写的类
- 双亲委派模型
- 其实就是一种询问机制, 保证一个类只会被加载一次!!!!!!!!!
7.反射
1.反射第一步:
得到你要操作的类的字节码对象:
- Class Class.forName(全类名); 全类名 = 包名 + 类名
- 类名.class
- Class c = 对象.getClass();
2.创建对象
- 1.先拿到构造函数
- Constructor[] getConstructors(); 获取所有的public修饰的构造函数
- Constructor[] getDeclaredConstructors(); 获取所有构造函数, 包含私有的
- Constructor getConstructor(Class ... args); 根据参数类型, 获取指定的构造,只能获取public修饰的
- Constructor getDeclaredConstructor(Class ... args); 根据参数类型, 获取指定的构造, 可以获取任何修饰符修饰的构造
- 2.通过构造函数创建对象
- Constructor
- T newInstance(Object ... args);
- void setAccessable(boolean isForce) true,false(默认值)
- 注意事项:
- 私有的构造必须设置暴力反射才行
- 简化的方式只适用于public修饰的无参构造
- 简化方式
- 类名 对象名 = 字节码对象.newInstance();
- Constructor
3.使用成员变量
- 1.先获取到成员变量对象
- Field[] getFields(); 获取所有的public修饰的构造函数
- Field getField(String name); 根据参数类型, 获取指定的构造,只能获取public修饰的
- Field[] getDeclaredFields(); 获取所有构造函数, 包含私有的
- Field getDeclaredField(String name); 根据参数类型, 获取指定的构造, 可以获取任何修饰符修饰的构造
- 2.使用成员变量
- Field
- set(对象,实际参数) 给对象的成员变量设置值
- Object get(对象) 获取对象上的成员变量的值
- 静态的成员变量不需要对象
- set(null,实际参数) 第一个参数传入null就行
- Field
4.使用成员方法
- 1.先获取到成员方法对象
- Method[] getMethods(); 获取所有的public修饰的构造函数
- Method[] getDeclaredMethods(); 获取所有构造函数, 包含私有的
- Method getMethod(String name,Class ... args);
- 根据参数类型, 获取指定的构造,只能获取public修饰的
- Method getDeclaredMethod(String name,Class ...args );
- 根据参数类型, 获取指定的构造, 可以获取任何修饰符修饰的构造
- 2.调用方法
- Method
- Object invoke(对象, 实际参数);
- 反射执行方法, 如果方法的返回值类型为void的话, 返回null
- Object invoke(对象, 实际参数);
- 静态的方法不需要对象
- Object invoke(null, 实际参数);
- 第一个参数传入null就行
- Object invoke(null, 实际参数);
- Method
8.枚举
JDK1.5出现的技术, 用来表示选项, 这些选项的特点是 不能进行数学运算, 因为他是引用数据类型, 本质就是一个类!!!!!!!!,类中可以定义的内容, 枚举中都可以定义,但是我们一般不会定义其他内容, 因为他的作用决定他的使用场景!!!!
定义格式:
-
public enum 枚举名{
枚举项1, 枚举项2 .......}
作用:
- 用来表示选项的,这些可以避免一些基本数学运算导致变量。
注意事项:
- 1.构造必须被private修饰, 如果我们不给构造, 系统会默认给定private 修饰的无参构造
- 2.如果你手动给了有参构造, 则不会在赠送的无参构造,需要显示调用你的有参构造
- 3.枚举项必须在第一行有效语句
- 4.如果枚举项下面没有东西了, 最后的分号可以省略, 如果有内容则不能省略,建议不要省!!!!!!
- 5.枚举中可以出现抽象方法, 但是枚举项需要使用匿名内部类的方式创建抽象类的子类!!!!!!