多线程学习
1.线程简介 线程实现(重点)
2. 线程状态 线程同步(重点)
01线程简介
: 任务,进程,线程,多线程
线程就是独立的执行路径;
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;umain()称之为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;u线程会带来额外的开销,如cpu调度时间,并发控制开销。
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
02线程创建
Thread、Runnable、Callable
(1)Thread 自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
(2)Runnable定义MyRunnable类实现Runnable接口,实现run()方法,编写线程执行体创建线程对象,调用start()方法启动线程
(3)实现Callable接口(了解即可)
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorServiceser=Executors.newFixedThreadPool(1);5.提交执行:Futureresult1=ser.submit(t1);
6.获取结果:booleanr1=result1.get()
7.关闭服务:ser.shutdownNow();
03线程状态
01:线程方法
04线程同步
处理多线程问题时 , 多个线程访问同一个对象 , 并且某些线程还想修改这个对象 . 这时候我们就需要线程同步 . 线程同步其实就是一种等待机制 , 多个需要同时访问 此对象的线程进入这个对象的等待池 形成队列, 等待前面线程使用完毕 , 下一个线程再使用。
由于同一进程的多个线程共享同一块存储空间 , 在带来方便的同时,也带来了访问 冲突问题 , 为了保证数据在方法中被访问时的正确性 , 在访问时加入 锁机制 synchronized , 当一个线程获得对象的排它锁 , 独占资源 , 其他线程必须等待 , 使用后释放锁即可 . 存在以下问题 : 一个线程持有锁会导致其他所有需要此锁的线程挂起 ; 在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引 起性能问题 ; 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒 置 , 引起性能问题 .
01同步方法
这套机制就是 synchronized 关键字 , 它包括两种用法 : synchronized 方法 和synchronized 块 .
同步方法 : public synchronized void method(int args) {}
同步块 : synchronized (Obj ) { }
Obj 称之为 同步监视器
Obj 可以是任何对象 , 但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器 , 因为同步方法的同步监视器就是this , 就是 这个对象本身 , 或者是 class [ 反射中讲解 ]
同步监视器的执行过程 1. 第一个线程访问 , 锁定同步监视器 , 执行其中代码 .
2. 第二个线程访问 , 发现同步监视器被锁定 , 无法访问 .
3. 第一个线程访问完毕 , 解锁同步监视器
4. 第二个线程访问, 发现同步监视器没有锁 , 然后锁定并访问
02锁
Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对 象来实现同步。同步锁使用Lock对象充当
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开 始访问共享资源之前应先获得Lock对象
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语 义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释 放锁。
synchronized 与 Lock 的对比
uLock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了 作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展 性(提供更多的子类)
优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方 法体之外)