文章目录
线程
注意!java并不能直接开启线程,java无法操作硬件,查看thread源码可以知道,它是通过调用一个本地c++方法实现的。
实现方式
-
通过继承thread类实现(拓展性不强,继承thread后就不能继承其他类了)
-
通过实现Runnable接口实现
-
实现callable类(企业级开发中常用,特点是重写的call()方法有返回值,有利于异常处理)
如何使用?
- 自定义类继承thread类或实现接口
- 重写run方法,编写执行体
- 新建线程实例,start()启动
//继承Thread类启动
Thread thread = new Thread(线程类); //创建线程实例
thread.setName("线程名称"); //一种规范
thread.start(); //启动
//实现Runnable接口启动
new Thread(传入对象,"线程名称").start(); //启动
并行与并发
-
并行:多人同行,多个线程同时执行,多核处理
-
并发:多个线程调用一个资源,通过快速交替执行模拟并行状态
线程与进程
-
进程:如qq,微信,Typora等
-
线程:比如Typora里定时自动更新的功能便是线程实现
-
一个进程包括多个线程,至少一个,java默认两个线程,main线程与GC线程
线程的五个状态
创建,就绪,运行,阻塞,死亡
new #创建
runnable #执行
blocked #阻塞
waiting #等待(等到为止)
time_waiting #超时等待(超过时间不再等待)
terminated #终止
Thread.getstate() #获取线程状态,以上6个
while(thread.getstate() != Thread.State.TERMINATED); #注意TERMINATED状态的线程无法再次启动
wait/sleep
- wait:等待会释放锁,只能在同步代码块中使用(加锁的方法),继承object类,需要捕获异常
- sleep:抱着锁睡觉,不会释放锁,在哪里都可以睡,继承至thread类
- 企业级开发中并不使用sleep,而是调用JUC的timeUnit方法
线程礼让yield
- 将线程的运行状态改为就绪状态
- 礼让不一定成功,看cpu心情
- 方法Thread.yield()
线程插队Join
- 插入线程后,执行完该线程再恢复原来的状态,如同插队打饭
- 方法Thread.yield()
线程优先级
- 使用数字1~10表示线程的优先级,默认优先级为5
- getPriority()和setPriiority()提供对线程的优先级获取和设置的方法
- 注意优先级高的不是一定先执行,而是增加了先执行的概率,优先级的设置建议在start前
守护线程
- 线程分用户线程(如main线程,默认线程都是守护线程)和守护线程(如gc垃圾回收线程)
- 虚拟机必须确保用户线程执行结束,而不需要等待守护线程结束
- thread.setDaemon(true)设置为守护线程,默认false用户线程,start前设置
- 守护线程常用于日志,监控内存,垃圾回收等
线程同步
- 一个对象被多个线程访问,即线程同步
- 保证并发安全性条件:队列+锁
- 实现线程同步使用排队机制,创建等待池
synchronized 锁(同步方法)
- 加锁能保证安全性,但降低了性能,且破坏线程的优先级
- 使用synchronized锁的是this,或者说class,我们往往需要锁的是对象,建议使用同步块synchronized(obj){ },这里obj是要上锁的对象,被称之为同步监视器。
Lock锁
-
jdk5开始出现的方法,继承于JUC
-
相比于隐式锁synchronized,lock锁是显示的,手动进行加锁解锁
-
lock锁运行效率上高于synchronized,且有很多子类,有更好的拓展性
-
lock是代码块锁,不能对方法或者类上锁
-
ReentrantLock可重入锁的使用
public TestLock implements Runnable{ //票的总数 private tickNums = 10; //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); //加锁 Thread.sleep(1000) //休眠1秒 System.out.println(tickNums--); } finally { lock.unlock(); //解锁 } } } }
线程协调(生产者消费者问题)
-
管程法:创建缓冲区,充当仓库的角色,产品不足通知生产者,有了产品通知消费者
-
信号灯法:设置标识符,通过状态通知生产者消费者
-
常用关键字
wait() //释放资源开始等待,进入等待池 wait(long timecut) //指定时间等待,单位毫秒 notify() //随机唤醒一个等待池(同一个对象上调用wait()方法的中的线程 notifyAll() //唤醒同一个对象上所有调用wait()方法的线程,根据优先级调用
线程并发库
-
目的是设置线程数量上限,防止线程过多导致缓慢或崩溃,线程池控制线程的创建与销毁
-
创建线程池有常用的四种线程池,通过JUC的Excutors提供方法
newFixdThreadPool() //指定最大并发数,超出的在队列中等待 newCachedThreadPool() //可缓冲线程池,长久不用的(60s)自动回收 newScheduledThreadPool() //指定最大并发数,可执行周期性,定时性任务执行 newSingleThreadExecutor() //单线程化的线程池
-
线程池使用方式
ExecutorService service = Excutors.newFixdThreadPool(10) //创建线程池连接,包含10个线程 //执行 service.excute(new Mythread()); service.excute(new Mythread2()); ....... service.shutdown(); //关闭连接
集合
存放对象的容器,容量可变,注意集合里存放的都是对象的引用,不能存放基本数据类型,基本数据类型要进行包装后才能存入。
集合框架
Collection
储存的都是value类型的
List
有序的,有下标,可重复,可为null
-
ArrayList
-
底层是数组实现,可自动扩容,一般是装满0.75扩容0.5倍
-
查询,修改效率高,插入,删除效率低
-
默认是线程不安全的,可以设置为线程安全
List lsit = Collections.synchronizedList(new ArrayList());
-
-
LinkedList
-
底层用双向链表实现的
-
插入,删除高效,查询,修改低效
-
线程不安全的
-
-
Vector
- 向量,可以理解为ArrayList()的同步实现
Set
不可重复
-
HashSet
- 底层使用HashMap实现的
- 无序,可有一个null
- 初始容量16
-
TreeSet
- 底层使用TreeMap实现的
- 插入时排序,不可为null
- 一个不同步的非线程安全的二叉树
Map
存放key-value类型的键值对
HashMap
-
底层由Hash表(数组结构)+链表实现的,jdk8后,默认长度为8时实现红黑树可排序
-
key无序的,不可重复,可有一个null,value无限制(判断key重复先比hashCode值,再调用equals()对比内容)
-
默认线程不安全的
-
HashTable与Hashmap类似,是线程安全的,但效率会降低
-
ConcurrentHashMap是用两者实现的,线程安全且效率高
TreeMap
- 底层通过红黑树实现
- 默认key自然顺序排序,不可重复,不能为null
- 可被克隆,可序列化
反射
所有框架底层的实现机制就是反射+注解
概述
-
理解:
-
java作为一门静态语言,因为有了反射,让它可以实现动态语言的功能,让java变成一门准静态语言
-
使用反射机制,允许我们在程序执行期间操作类的内部信息,包括私有化属性,方法(所以java不存在真正意义上的封装)
-
反射会消耗大量的性能,大概是new的几十倍,使用需要谨慎
Class
-
不管创建多少个类的实例,jvm中都只有一个它的Class,例如我们写了一个类,如果没有new一个对象实例化,是没有生成.class文件的,只有我们程序中实例化了对象,且不管实例化多少个,都只有一个对应的.class文件。
-
通过Class可以获得这个类的所有内部信息,结构
-
几乎所有类型都有Class对象,包括接口,[]数组,enum枚举,注解,基本数据类型,甚至void都有Class对象
-
获取Class实例
//com.howe.entity下定义一个Student类继承Person类 Person person = new Student(); //方法一:通过实例对象获取Class Class c1 = person.getClass(); //方法二:通过forname获取Class Class c2 = Class.forname("com.howe.entity.Student"); //需要抛出异常 //方法三:通过类名.class属性获取 Class c3 = Student.class; //方法四:一些类(如基本数据类型的封装类)可以通过.TYPE获取 Class c4 = Integer.TYPE //获取Class父类的Class Class c5 = c1.getSuperclass();