Map
Map
特点 key-value映射
HashMap
Key无序 唯一(Set)
value 无序 不唯一(Collection)
实现方式:数组+链表(jdk1.7的时候)
实现方式:数组+链表+红黑树(jdk1.8之后)
LinkedHashMap
有序的HashMap 速度快
实现方式:链表
TreeMap
有序 速度没有hash快
实现方式:红黑树
基本api操作:
增加:put(k,v)
查找:isEmpty()
清空集合中的所有元素:clear()
Map.entry:表示的是K-V组合的一组映射关系,key和value成组出现。
HashMap和HashTable的区别:
hashmap线程不安全,效率比较高,hashtable线程安全,效率低。
hashmap中key和value可以为空。hashtable键和值都不可以为空。
HashMap源码:每次快满还没满的时候要进行扩容操作。
很多数字是为了减少哈希碰撞。
hash初始值为2的N次幂:
方便进行&操作,提高效率,&要比取模运算效率要高。
hash&(initCapacity-1)
在扩容之后涉及到元素的迁移过程,迁移的时候只需要判断二进制的前一位是0或者是1即可。如果是0,表示新数组和旧数组的下标位置不变,如果是1,只需要将索引位置加上旧数组的长度值即为新数组的下标。
1.7源码知识点:
默认初始容量
加载因子
put操作
1.(设置值,计算hash)
2.扩容操作
3.数据迁移的过程
jdk1.8:当链表长度超过8时,链表会转换成红黑树,方便查找。
Collections及File类讲解
Collections工具类:
Collections和Collection不同,前者是集合的操作类,后者是集合接口。
Collections提供的静态方法:
addAll():批量添加 Collecctions.addAll();
sort():排序
binarySeach():二分查找
fill():替换 填充。把所有元素都填充成一个元素
shuffle():随机排序
reverse():逆序
Arrays:
Arrays提供了数据操作的工具类,包含很多方法:
集合和数组之间的转换 数组转成list
集合总结:
File:
File类提供了对当前文件系统中文件的部分操作。
创建文件 newFile()
判断文件的属性 canRead() ,都会返回boolean类型的值
判断当前文件是否存在 file.exists()
获取文件的名称file.getName()
获取文件的绝对路径
获取文件的父路径名称,如果文件的路径中只包含文件名称,则显示空。
无论当前文件是否存在,只要给定具体的路径,都可以返回相应的路径名称。
判断文件是否是文件或者目录
打印当前文件系统的所有盘符 File.listRoots()
创建单级目录 file.mkdir()
创建多级目录 file.mkdirs()
//循环遍历输出c盘中的所有文件的绝对路径。
//文件在遍历的时候,会出现空指针的问题,原因在于当前文件系统受保护,某些文件没有访问权限,此时会报空指针异常。
IO流
当写完程序时一定要注意关闭流。
步骤:
选择合适的io流对象
创建对象
传输数据
关闭流对象(会占用系统资源)
优化:添加缓冲区,而不是每次只读一个字节,缓冲区就用缓冲数组来表示,每次会将数据添加到缓冲区中,当缓冲区满了之后,一次读取。
字符流可以直接读取中文汉字,而字节流在处理的时候会出现中文乱码。
Java多线程
线程的概念
程序:是一个指令的集合
进程:是一个静态的概念(正在执行中的程序)作为资源分配的单位
进程时程序的一次静态执行过程,占用特定的地址空间。(占有一定的资源)
线程:是进程中一个“单一的连续控制流程”/执行路径。 线程又被称为轻量级进程。(调度执行的单位)
一个进程可以拥有多个并行的线程。
实现线程的方式
- 继承Thread类,调用start方法。
public class XianCheng extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(i); } } }
实现Runnable接口:(使用了代理模式)
虽然使用的thread类来执行,但是使用的却是runnable里面的东西。
不需要给共享变量添加static关键字,每次创建一个对象,作为共享对象即可
class Xiancheng2 implements Runnable{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(i); } } }
线程的代理设计模式:
举例:
潘金莲和西门庆勾结,但是不敢直接勾结,需要王婆作为代理。表面上是王婆联系的,但是是潘金莲在做事。
定义一个顶层的接口:(代理人和被代理人都有的行为)
public interface KindWomen { public void playWithMan(); public void makeEyesWithMan(); }
潘金莲这个实体类,也是被代理人:
public class PanJinLian implements KindWomen{ @Override public void playWithMan() { System.out.println("潘金莲。。。。"); } @Override public void makeEyesWithMan() { System.out.println("潘金莲在和男人抛媚眼"); } }
王婆这个代理人(表面上是王婆在做事,实际上是被代理人在做事):
public class WangPo implements KindWomen{ private KindWomen kindWomen; public WangPo(){//默认用潘金莲作为代理 this.kindWomen=new PanJinLian(); } public WangPo (KindWomen kindWomen){ this.kindWomen=kindWomen; } @Override public void playWithMan() { this.kindWomen.playWithMan();//自己干不动了让别人去干 } @Override public void makeEyesWithMan() { this.kindWomen.makeEyesWithMan(); } }
线程的生命周期
创建
就绪
(阻塞)
运行
终止
1.新生状态:
当创建好当前线程对象之后,没有启动之前(调用start方法之前)
ThreadDemo thread=new ThreadDemo()
RunnableDemo run=new RunnableDemo()
2.就绪状态:
准备开始执行,并没有执行,表示调用start方法之后
当对应的线程创建完成,且调用start方法之后,所有的线程会添加到一个就绪队列中,所有的线程同时去抢占cpu的资源。
3. 运行状态:
当当前进程获取到cpu资源之后,就绪队列中的所有线程会去抢占cpu的资源,谁先抢占到谁先执行,在执行的过程中就叫做运行状态。抢占到cpu资源,执行代码逻辑开始。
4. 死亡状态:
当运行中的线程正常执行完所有的代码逻辑或者因为异常情况导致程序结束叫做死亡状态。
进入的方式:
1.正常运行完成且结束
2.认为中断执行,比如使用stop方法
3.程序抛出未捕获的异常
5. 阻塞状态:
在程序运行过程中,发生某些异常情况,导致当前线程无法再顺利执行下去,此时会进入阻塞状态,进入阻塞状态的原因消除之后,所有的阻塞队列会再次进入到就绪状态中,随机抢占cpu的资源,等待执行。
进入的方式:sleep方法 等待io资源
介绍线程类api方法
public static void main(String[] args) { //获取当前线程对象 Thread thread=Thread.currentThread(); //获取当前线程的名称 System.out.println(thread.getName()); //获取线程的id System.out.println(thread.getId()); //获取线程的优先级,在一般系统中范围是0-10的值,如果没有经过设置的话,就是默认值5,也可以自己设置。 System.out.println(thread.getPriority()); //设置线程池的优先级 /** * 优先级越高一定越优先执行嘛? * 不一定,只是优先级越高执行的概率越大。 */ thread.setPriority(10); System.out.println(thread.getPriority()); }
public final void join() 调用该方法强制执行
public static void sleep() 使正在执行的线程休眠millis秒
public static void yield() 当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态 ,当前线程就会马上恢复执行。
public final void stop() 强迫线程停止执行。已过时,不推荐使用。
注意:在多线程的时候,可以实现唤醒和等待的过程,但是唤醒和等待操作的对应不是thread类,而是我们设置的共享对象和共享变量。
阻塞状态:有三种方法可以暂停Thread执行:
sleep:不会释放锁,sleep时的线程也不可以访问锁定对象。
yield:让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
join:当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。
多线程并发访问的时候会出现数据安全问题:
解决方式:
同步代码块
synchronized(共享资源,共享对象,需要的是object的子类)(具体执行的代码块)
2. 同步方法:
将核心的代码逻辑定义成一个方法,使用synchronized关键字进行修饰,
死锁
同步可以保证资源共享操作的正确性,但是过多同步也会产生死锁。
死锁一般情况下表示互相等待,是程序运行时出现的一种问题。
Java线程池
使用线程池管理线程主要有如下好处:
使用线程池可以重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗
由于没有线程创建和销毁时的消耗,可以提高系统响应速度
通过线程可以对线程进行合理的管理,根据系统的承受能力可运行线程数量的大小等
线程池工作原理:
线程池执行所提交的任务过程:
先判断线程池中核心线程池所有的线程是否都在执行任务。如果不是,则新创建一个线程执行刚提交的任务,否则,核心线程池中所有的线程都在执行任务,则进入第2步。
判断当前阻塞队列是否已满,如果未满,则将提交的任务放置在阻塞队列中;否则,则进入第3步。
判断线程池中所有的线程是否都在执行任务,如果没有,则创建一个新的线程来执行任务,否则,则交给饱和策略进行处理
设计一个线程类:
public class Task implements Runnable{
线程池的使用:
- CacheThread:线程个数没有限制
public class CacheThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for(int i=0;i<20;i++){ executorService.execute(new Task());//线程执行,进行了一个线程的复用 } //线程关闭 executorService.shutdown(); } }
- FixedThread:线程是可以自己指定的
public class FixedThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5);//最多只允许5个线程数量执行 for(int i=0;i<20;i++){ executorService.execute(new Task()); } executorService.shutdown(); } }
newSingleThreadExectue:线程是单一的
public class SingleThreadPoolDemo { public static void main(String[] args) { //一个线程 ExecutorService executorService = Executors.newSingleThreadExecutor(); for(int i=0;i<20;i++){ executorService.execute(new Task()); } executorService.shutdown(); } }