多线程
多线程可以从以下几个方面学习:
什么是进程? 什么是线程? 什么是线程安全?如何确保线程安全? 如何实现多线程?
什么是进程?
电脑会单独的运行许多的程序,每个程序都是一个独立的进程,每个进程之间都是独立存在的。如下图QQ、钉钉、有道云笔记都是一个独立的进程。
什么是线程?
线程是程序中执行的线程,java虚拟机允许程序同时运行多个执行线程。
进程的执行依赖线程。进程的最小执行单位是线程,一个进程种至少有一个线程。
线程有6种状态:新建(NEW),运行(RUNNABLE),阻塞(BLOCKED),等待(WAITING),计时等待(TIMED_WAITING)和终止(TERMINATED)。
什么是多线程?
提到多线程主要搞明白两个概念 ,串行和并发,搞清楚这个,才能更好地理解多线程。
串行,也就是单线程,当我们在下载多个文件地时候,在串行中他是按照一定地顺序进行下载,也就是说当A文件下载完,才能进行B
文件的下载。
并行,下载多个文件的时候,开启多条线程,多个文件同时进行下载,也就是说,在同一个时间发生的事情。
什么是线程安全?
当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
如何确保线程安全?
Synchronized关键字,用来控制线程同步的,确保在多线程的环境下,不被多个线程同时执行,确保数据的完整性,一般加在方法上。Synchronized锁的是方法括号中的对象,当锁住一个对象之后,别的线程想要获取对象,那么就必须等这个线程执行完释放锁对象之后才可以,否则一直处于等待状态。
Lock 通过手动的获取锁和释放锁
写个主方法测试程序是否正常
输出结果
如何实现多线程?
创建线程对象方法
1、Thread
自定义线程类基础Thread
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
练习
2.Runnable接口
小结
继承Thread类
子类继承Thread类具备多线程能力
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
实现Runnable接口
实现Runnable接口具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一对象被多个线程使用
实例
3.实现Callable接口
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorService = Executor.newFixedThreadPool(1);
5.提交执行:Future<Boolean> result1 = ser.submit(t1);
6.获取结果:boolean r1 = result.get()
7.关闭服务
静态代理模式总结
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
Lambda表达式
避免匿名内部类定义过多
其实质属于函数时编程的概念
可以让代码看起来简洁
去掉了一堆没有意义的代码,只留下核心的逻辑
函数式接口
Functional Interface(函数式接口)
定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
对于函数式接口,可以通过Lambda表达式来创建该接口的对象。
总结:
1.Lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
2.前提式接口为函数式接口
3.多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
线程停止
线程休眠
sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间到达后线程进入就绪状态
sleep可以模拟网络延时,倒计时等
每一个对象都有一个锁,sleep不会释放锁
线程礼让
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态为就绪状态
让cpu重新调度,礼让不一定成功!看cpu心情
join
join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
可以想象成插队
线程状态观测
Thread.State
线程状态,线程可以处于以下状态之一:
new 尚未启动的线程处于此状态
Runnable再java虚拟机中执行的线程处于此状态
Blocked 被阻塞等待监视器锁定的线程处于此状态
Waiting 正在等待另一个线程执行特定动作的线程处于此状态。
Timed Waiting 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
一个线程可以给定时间点处于一个状态。这些状态时不反应任何操作系统线程状态的虚拟机状态
线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有1线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10
使用以下方式改变或获取优先级
getPriority().setPriority(int xxx)
注意:先设置优先级,再start线程
守护(daemon)线程
线程分为用户线程和守护线程
虚拟机必须确保用户现场执行完毕
虚拟机不用等待守护线程执行完毕
如,后台记录操作日志,监控内存,垃圾回收等待。。。
线程同步机制
线程同步
由于同一进程的多个线程共享同一块存储空间,再带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被
访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,
使用后释放锁即可,存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起;
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引起性能问题。
线程锁
//不安全的买票
//不安全取钱
//两个人去银行取钱,同一个账户
同步块
Synchronized(Obj){}
Obj称之为同步监视器
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者class
同步监视器的执行过程
1.第一个线程访问,锁定同步监视器,执行其中代码
2.第二个线程访问,发现同步监视器被锁定,无法访问
3.第一个线程访问完毕,皆出同步监视器
4.第二个线程访问,发现同步监视器没有锁
死锁避免方法
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不妨。
3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
4.循环等待条件:等若干进程之间形成一种头尾相接的循环等待资源关系。
Lock锁
- JDK5.0开始,java提供了更强哥的同步机制——通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当
- Java.util.concurrent.locks.Lock接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前先获得Lock对象
- ReentranLock类实现了Lock,它拥有与synchronized相同的并发性和内存予以,在实现线程安全的控制中,比较常用的时ReentrantLock,可以显示加锁、释放锁。
Synchronized与Lock的对比
· Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
· Lock只有代码块加锁,synchronized有代码块锁和方法锁
· 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
· 优先使用顺序:
· Lock ->同步代码块(已经进入了方法体,分配了想要资源)->同步方法(在方法体之外)
线程通信
应用场景: 生产者和消费者问题
· 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
· 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。
· 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
//信号灯法
使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发状态下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活
中的公共·交通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
便于线程管理(…)
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务是最多保持多长时间会终止
JDK5.0起提供了线程池相关API:
ExecutorService
Executors
ExecutorService:
真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command): 执行任务/命令,没有返回值,一般用来执行Runnable
<T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
Void shutdown(): 关闭连接池
Executors:工具类、线程池得工厂类,用于创建并返回不同类型的线程池
线程总结