创建线程
-
继承Thread类
-
重写run方法
-
调用start方法
runnable使用了静态代理模式
-
真实对象和代理对象都要实现同一个接口
-
代理对象要代理真实角色
thread
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
Runnable
-
实现接口Runnable 具有多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
比较推荐使用,避免单继承的局限性,灵活方便,方便再同一个对象被多个线程使用
静态代理模式
-
真实对象和代理模式都要实现同一个接口
-
代理对象要代理真实角色
停止线程
-
建议线程正常停止--利用次数不建议死循环
-
使用标志位,设置一个标志位
-
不要使用stop或者destroy等过时的方法
private boolean flag=true;//设置标志位 public static void main(String[] args) {//主函数入口 TestThread1 testThread1=new TestThread1(); new Thread(testThread1).start(); for (int i = 0; i < 1000; i++) { System.out.println("main"+i); if(i==400) {//到达某个值,将标志位置为假,以此来停止线程 testThread1.Stop(); System.out.println("线程停止了"); } } } @Override public void run() { int i=0; while (flag){ System.out.println("我还在运行"+i++); } } public void Stop(){ //停止线程方法 this.flag=false; }
线程休眠sleep
-
sleep 指定当前线程阻塞的毫秒数
-
sleep存在异常,需要捕获
-
sleep时间到达后线程进入就绪状态
-
sleep可以模拟网络样式,倒计时等
-
每个对象有一个锁,sleep不会释放锁
线程礼让-yield
-
礼让线程,让当前正在执行的线程暂停,但不堵塞
-
将线程从运行状态转为就绪状态
-
让cpu重新调度,礼让不一定成功
线程状态
-
new(新生)、runnable、bloked、waiting、time_waiting、terminated(退出)
线程的优先级
-
java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
守护(daemon)线程
-
线程分为用户线程和守护线程
-
虚拟机不用等待守护线程执行完毕
-
如,后台记录操作日志,监控内存,垃圾回收等待
public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread1=new Thread(god); thread1.setDaemon(true);//设置为守护线程,默认为flase thread1.start(); new Thread(you).start(); } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑你~~~"); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("我开心的活着~~"); } } }
线程同步 -重点
-
加入锁机制 synchronized
形成条件:队列+锁
同步方法
public synchronized void method(){ }
-
缺陷:将一个大的方法声明为synchronized会影响效率
同步块
synchronized(obj){ }
-
synchronized默认锁的是本身
锁 lock
lock.lock();//加锁 lock.unlock; //解锁
-
注意:wait() 会释放锁, 而sleep不会释放锁
死锁的四个必要条件
-
互斥条件
-
请求和保持条件
-
不剥夺条件
-
循环等待条件
管程法
线程池
8锁现象
-
synchronized锁方法时,锁的是方法的调用者
如:同一对象调用两个不同的同步方法,也得按顺序执行
-
static加上synchronized会导致锁的变成是类对象
-
如一个静态同步方法,一个普通同步方法,由于第一个锁的是class模板,第二个锁的是调用的对象,是不同的锁,因此不需要等待
集合类不安全
写入时复制--优化的策略,效率降低不会太多
写入时复制(CopyOnWrite,简称COW)思想是计算机程序设计领域中的一种优化策略。其核心思想 是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共 同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用 副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的 调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本 (private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。 读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
hashset 底层是hashmap
Callable实现原理 使用适配类-适配器模式
三个工具类
减法计数器-加法计数器-信号量
读写锁
阻塞队列
同步队列
线程池
考点:三大方法、七大参数、四种拒绝策略
三大方法
三种线程池各有什么特点‘
七大参数
corePollSize :核心线程数。在创建了线程池后,线程中没有任何线程,等到有任务到来时才创建 线程去执行任务。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建 一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当 中。
maximumPoolSize :最大线程数。表明线程中最多能够创建的线程数量,此值必须大于等于1。
keepAliveTime :空闲的线程保留的时间。
TimeUnit :空闲线程的保留时间单位。 BlockingQueue< Runnable> :阻塞队列,存储等待执行的任务。参数有ArrayBlockingQueue、 LinkedBlockingQueue、SynchronousQueue可选。 ThreadFactory :线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler :队列已满,而且任务量大于最大线程的异常处理策略。
四大函数接口
Stream流式计算
分支合并-Forkjoin
其中,工作窃取可以提高效率
异步回调
JMM
可见性--让其他线程可以看到主内存发生的变化
JMM八大指令
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量 才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机 遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的 变量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存 中,以便后续的write使用
write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内 存的变量中
a++不是原子性操作
可以使用原子类解决原子性问题
深入单例模式
-
饿汉式
一开始就将对象创建出来
-
懒汉式
当有线程调用时再创建对象
-
DCL懒汉式
增加双层锁验证--
不是原子性操作,至少会经过三个步骤:
-
分配对象内存空间
-
执行构造方法初始化对象
-
设置instance指向刚分配的内存地址,此时instance !=null;
-
由于指令重排,导致A线程执行 lazyMan = new LazyMan();的时候,可能先执行了第三步(还没执行第 二步),此时线程B又进来了,发现lazyMan已经不为空了,直接返回了lazyMan,并且后面使用了返回 的lazyMan,由于线程A还没有执行第二步,导致此时lazyMan还不完整,可能会有一些意想不到的错 误,所以就有了下面一种单例模式。
-
下一种在上面DCL单例模式增加一个volatile关键字来避免指令重排:
-
静态内部类版--会被反射破坏
-
使用枚举--最终版
深入理解CAS
简单的说:比较当前值是否还是原来的,如果为true,则修改
CAS:比较并交换 --compareAndSet
核心Unsafe类
底层使用了自旋锁,
缺点:
-
循环会耗时
-
一次性只能保证一个共享变量的原子性
-
ABA问题
CAS:ABA问题
简单说:线程A读取了内存某块数据,接着线程B也读取了这块内存数据并将该值改了,后面又改了回来。这时候A线程发现数据还是读取时的数据,因此更新数据,而不知道再读取后这块内存已经被修改过--
解决办法
增加版本号-类似乐观锁的方式
版本号原子引用
坑:如果约定的泛型时包装类,要注意引用的对象是否相同!!!
正常业务中比较的是业务的对象,不存在引用不同的问题
Java各种锁
公平锁
线程按照申请锁的顺序执行,不存在插队
默认是非公平锁
可重入锁
指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法 获取锁的时候,在进入内层方***自动获取锁。
自旋锁
例子:原子引用
死锁
产生死锁主要原因:
1、系统资源不足
2、进程运行推进的顺序不合适
3、资源分配不当
解决方法:
-
使用 jsp -l 定位进程号
-
使用 jstack +进程号 查看堆栈信息
全部评论
(0) 回帖