线程
2020-06-10 17:26:06 10 举报
AI智能生成
线程是计算机中的一种执行单元,它是程序中的一个独立执行路径。每个线程都有自己的堆栈、寄存器和局部变量等资源,可以并行运行多个任务。线程的创建和管理由操作系统负责,程序员可以通过线程库提供的API来创建、调度和管理线程。线程可以提高程序的并发性和响应性,使得程序能够同时处理多个任务。但是,多线程也会带来一些问题,如竞争条件、死锁等,需要程序员仔细设计和调试。在现代计算机系统中,线程已经成为一种重要的编程模型和技术手段,被广泛应用于各种领域。
作者其他创作
大纲/内容
线程
2.基础线程机制
Executor
CachedThreadPool:一个任务创建一个线程
FixedThreadPool:所有任务只能使用固定大小的线程
SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool
Daemon
在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程
sleep()
sleep() 可能会抛出 InterruptedException,因为异常不能跨线程传播回 main() 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理
yield()
对静态方法 Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。
4.互斥同步
synchronized
1.同步一个代码块
它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。对于以下代码,使用 ExecutorService 执行了两个线程,由于调用的是同一个对象的同步代码块,因此这两个线程会进行同步,当一个线程进入同步语句块时,另一个线程就必须等待。
2. 同步一个方法
它和同步代码块一样,作用于同一个对象。
3. 同步一个类
作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步
4. 同步一个静态方法
作用于整个类
ReentrantLock
例子
比较
1. 锁的实现
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
2. 性能
新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
3. 等待可中断
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。ReentrantLock 可中断,而 synchronized 不行。
4. 公平锁
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
5. 锁绑定多个条件
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
6.线程状态
新建(NEW)
可运行(RUNABLE)
阻塞(BLOCKED)
无限期等待(WAITING)
限期等待(TIMED_WAITING)
死亡(TERMINATED)
Java 内存模型
主内存与工作内存
线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成
内存间交互操作
read:把一个变量的值从主内存传输到工作内存中
load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
use:把工作内存中一个变量的值传递给执行引擎
assign:把一个从执行引擎接收到的值赋给工作内存的变量
store:把工作内存的一个变量的值传送到主内存中
write:在 store 之后执行,把 store 得到的值放入主内存的变量中
lock:作用于主内存的变量
unlock
内存模型三大特性
1. 原子性
2. 可见性
3. 有序性
先行发生原则
单一线程原则
在一个线程内,在程序前面的操作先行发生于后面的操作
管程锁定规则
一个 unlock 操作先行发生于后面对同一个锁的 lock 操作
volatile 变量规则
对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作
线程启动规则
Thread 对象的 start() 方法调用先行发生于此线程的每一个动作
线程加入规则
Thread 对象的结束先行发生于 join() 方法返回。
线程中断规则
对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生
对象终结规则
一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始
传递性
如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C
12.锁优化
自旋锁
锁消除
锁粗化
轻量级锁
偏向锁
1.使用线程
实现 Runnable 接口
需要实现接口中的 run() 方法
使用 Runnable 实例再创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程
实现 Callable 接口
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装
使用
继承 Thread 类
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
3.中断
InterruptedException
interrupted()
Executor 的中断操作
5.线程之间的协作
join()
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。
wait() notify() notifyAll()
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程
wait() 和 sleep() 的区别
wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;wait() 会释放锁,sleep() 不会。
await() signal() signalAll()
相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。
7.J.U.C - AQS
CountDownLatch
用来控制一个或者多个线程等待多个线程。维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。
CyclicBarrier
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。
Semaphore
Semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数。
FutureTask
BlockingQueue
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现
FIFO 队列 :LinkedBlockingQueue、ArrayBlockingQueue(固定长度)
优先级队列 :PriorityBlockingQueue
提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置
ForkJoin
主要用于并行计算中,和 MapReduce 原理类似,都是把大的计算任务拆分成多个小任务并行计算。
十一、线程安全
不可变
final 关键字修饰的基本数据类型
String
枚举类型
Number 部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和 BigDecimal 等大数据类型。但同为 Number 的原子类 AtomicInteger 和 AtomicLong 则是可变的
互斥同步
synchronized 和 ReentrantLock
非阻塞同步
CAS
AtomicInteger
J.U.C 包里面的整数原子类 AtomicInteger 的方法调用了 Unsafe 类的 CAS 操作。
ABA
0 条评论
回复 删除
下一页