锁对象的功能
锁对象 : 锁对象可以控制线程! 保证线程执行的安全性!
特点: 1. 被所有的线程对象共享 2. 锁对象必须是引用数据类型
锁对象的功能来自于Object 类:
唤醒线程的方法:
void notify ( ) : 随机唤醒一个正在沉睡/ 等待状态的线程
void notifyAll ( ) : 唤醒所有正在沉睡/ 等待状态的线程
让线程等待的方法:
void wait ( ) : 让线程永久等待 -> 无限等待
void wait ( long timeout) : 让线程在一定时间内等待 -> 限时等待
1. 多线程代码没有同步, 没有锁 : 线程只需要抢到CPU执行权就可以执行
2. 多线程代码有同步, 有锁 : 线程需要同时抢到CPU执行权和锁资源才能执行
wait和sleep的区别
sleep : 持有锁进行休眠, 只会释放CPU资源 -> 抱着锁睡!
wait : 同时释放锁资源和CPU执行权
线程的生命周期
新建 NEW : 至今尚未启动的线程处于这种状态。 -> new 过了, 但是没有调用start方法
运行 RUNNABLE : 正在 Java 虚拟机中执行的线程处于这种状态
死亡 TERMINATED : 已退出的线程处于这种状态
阻塞 BLOCKED : 线程没有锁资源或者没有CPU资源的状态 -> 醒着
限时等待 TIMED_WAITING : 线程属于昏迷状态( 没有资格抢夺CPU资源或者锁资源) , 时间到自己醒
被调用 wait ( long timeout) 或者 sleep ( long millis)
无限等待 WAITING : 线程属于昏迷状态( 没有资格抢夺CPU资源或者锁资源)
被调用 void wait ( )
等待唤醒案例
等待唤醒案例 : 线程的通讯, 生产者和消费者案例
厨师 : 线程
顾客 : 线程
包子 : 可以控制线程( 它的状态决定了线程的执行状态) -- > 锁, 控制线程的对象, 增加程序的趣味性
线程池
线程池 装线程的池子
线程池对象的类型 : ThreadPoolExecutor
线程池的创建方式一: 面向对象的方式
工具类: Executors
创建线程池对象的方法:
static ExecutorService newCachedThreadPool ( ) : 创建不指定最大线程数量的线程池对象( 底层也是由最大线程数量的: 21 个亿多)
static ExecutorService newFixedThreadPool ( int nThreads) : 创建一个指定最大线程数量的线程池对象
向线程池提交任务:
Future < ? > submit ( Runnable task) : 向线程池提交一个不带任务结果的线程任务
< T > Future < T > submit ( Callable < T > task) : 向线程池提交一个带任务结果的线程任务
< T > Future < T > submit ( Runnable task, T result) 向线程池提交一个不带任务结果的线程任务, 手动编写结果 ( T result)
线程池的创建方式二: 面向过程的方式
ThreadPoolExecutor : 线程池对象的类
面向过程的方式 : 手动创建ThreadPoolExecutor 对象
ThreadPoolExecutor ( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue < Runnable > workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
TimeUnit unit
TimeUnit unit : 时间单位的枚举类
枚举类的使用 : 创建枚举对象的方式 -> 枚举的类名. 对象名;
DAYS
HOURS
MICROSECONDS
MILLISECONDS
MINUTES
NANOSECONDS
SECONDS
例如: TimeUnit . SECONDS -> 创建此类的对象
BlockingQueue workQueue
BlockingQueue < E > : 单列集合, 继承了Collection < E > -> 自己也是接口
使用其具体的实现类: ArrayBlockingQueue < E >
构造方法:
ArrayBlockingQueue ( int capacity)
int capacity: 阻塞队列中最多的元素数量
增删改查四类功能: 参考Collection
跟队列相关的方法:
进队列 : void put ( E e) 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。
出队列 : E take ( ) 获取并移除此队列的头部,如果队列中已经没有元素了, 如果继续take那就等。
ThreadFactory threadFactory
ThreadFactory threadFactory 线程工厂 -> 人才市场
借助工具类型 : Executors
static ThreadFactory defaultThreadFactory ( ) 返回用于创建新线程的默认线程工厂。
RejectedExecutionHandler handler
RejectedExecutionHandler handler : 等待拒绝策略
RejectedExecutionHandler 接口, 不能创对象 -- > 需要它的实现类对象
RejectedExecutionHandler 的实现类对象是 ThreadPoolExecutor 的静态内部类对象!
静态内部类对象如果创建 :
class Outer {
static class Inner {
}
}
Outer. Inner inner = new Outer. Inner ( ) ;
分类 :
static class ThreadPoolExecutor. AbortPolicy : 默认方法和推荐方案 -> 直接拒绝并报错!
new ThreadPoolExecutor. AbortPolicy ( ) ;
static class ThreadPoolExecutor. DiscardPolicy : 不推荐 -> 直接拒绝
new ThreadPoolExecutor. DiscardPolicy ( ) ;
static class ThreadPoolExecutor. DiscardOldestPolicy : 随机移除阻塞队列中的某个元素, 把不能服务的等待最久的元素添加到阻塞队列中
new ThreadPoolExecutor. DiscardOldestPolicy ( ) ;
static class ThreadPoolExecutor. CallerRunsPolicy : 找其他的线程帮助服务不能服务的线程任务
new ThreadPoolExecutor. CallerRunsPolicy ( ) ;
volatile关键字
案例:
class Money {
public static int money = 100000 ;
}
class MyThread1 extends Thread {
@Override
public void run ( ) {
while ( Money . money == 100000 ) {
}
System . out. println ( "结婚基金已经不是十万了" ) ;
}
}
class MyThread2 extends Thread {
@Override
public void run ( ) {
try {
Thread . sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
Money . money = 90000 ;
}
}
volatile 关键字 : 不稳定的, 易变的
被其修饰的变量, 会强制要求线程对象去内存中找真实值, 而不是去线程栈中找临时变量值! !
解决方案1 : 在Money 变量前面加volatile 关键字
解决方案2 : 上锁, 同步操作!
上锁的特点: 一个线程操作共享数据的时候, 不让其他的线程对象去使用. 线程对象就会有足够的时间去内存中找共享数据的真实值; ( 优先去内存中找)
public class VolatileDemo {
public static void main ( String [ ] args) {
MyThread1 小红 = new MyThread1 ( ) ;
MyThread2 小刚 = new MyThread2 ( ) ;
小红. start ( ) ;
小刚. start ( ) ;
}
}
class Money {
public static Object lock = new Object ( ) ;
public static int money = 100000 ;
}
class MyThread1 extends Thread {
@Override
public void run ( ) {
while ( true ) {
synchronized ( Money . lock) {
if ( Money . money != 100000 ) {
System . out. println ( "结婚基金已经不是十万了" ) ;
break ;
}
}
}
}
}
class MyThread2 extends Thread {
@Override
public void run ( ) {
synchronized ( Money . lock) {
try {
Thread . sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
Money . money = 90000 ;
}
}
}
原子性和非原子性
原子性操作 : 不可以分割的操作 -> 原子性操作
例如 : 赋值操作 int a = 10 ;
非原子性操作 : 可以分割的操作
例如: 自增自减 a++ ; -> 1. a + 1 2. a = a+ 1 ;
例如: 拓展的赋值运算符 a += 10 ; 1. a + 10 2. a = a + 10 ;
volatile 解决不了非原子性操作的线程安全问题! -- > volatile 只能解决原子性操作的线程安全问题
volatile关键字不能解决非原子性操作的安全问题
同步代码块解决非原子性操作的线程安全问题
package com. atguigu. d_atom ;
public class AtomDemo1 {
public static void main ( String [ ] args) {
Target1 target = new Target1 ( ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
new Thread ( target) . start ( ) ;
}
}
}
class Target1 implements Runnable {
private int count = 0 ;
private Object obj = new Object ( ) ;
@Override
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
synchronized ( obj) {
count++ ;
System . out. println ( "已经送了" + count + "个冰淇淋" ) ;
}
}
}
}
原子性类_AtomicXxxxx
概述:Java 从JDK1. 5 开始提供了java. util. concurrent. atomic包( 简称Atomic 包) ,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。( CAS算法 + 自旋)
因为变量( 共享数据) 的类型有很多种,所以在Atomic 包里一共提供了13 个类,属于4 种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性( 字段) 。
本次我们只讲解使用原子的方式更新基本类型,使用原子的方式更新基本类型Atomic 包提供了以下3 个类:
1. AtomicBoolean :原子更新布尔类型
2. AtomicInteger :原子更新整型
3. AtomicLong :原子更新长整型
AtomicInteger 原理 : 自旋锁 + CAS 算法
CAS算法:
有3 个操作数(内存值V , 旧的预期值A ,要修改的值B )
当旧的预期值A == 内存值 此时修改成功,将V 改为B
当旧的预期值A != 内存值 此时修改失败,不做任何操作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
AtomicInteger:原子更新整型
public AtomicInteger ( ) :初始化一个默认值为0 的原子型Integer
public AtomicInteger ( int initialValue) :初始化一个指定值的原子型Integer
int get ( ) : 获取值
int getAndIncrement ( ) : 以原子方式将当前值加1 ,注意,这里返回的是自增前的值。 b= a++ ;
int incrementAndGet ( ) : 以原子方式将当前值加1 ,注意,这里返回的是自增后的值。 b= ++ a;
int addAndGet ( int data) : 以原子方式将输入的数值与实例中的值(AtomicInteger 里的value)相加,并返回结果。
int getAndSet ( int value) : 以原子方式设置为newValue的值,并返回旧值。
通过具备原子性的类解决非原子性操作的线程安全问题
package com. atguigu. d_atom ;
import java. util. concurrent. atomic. AtomicInteger ;
public class AtomDemo2 {
public static void main ( String [ ] args) {
Target2 target = new Target2 ( ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
new Thread ( target) . start ( ) ;
}
}
}
class Target2 implements Runnable {
private AtomicInteger count = new AtomicInteger ( ) ;
@Override
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
count. incrementAndGet ( ) ;
System . out. println ( "已经送了" + count + "个冰淇淋" ) ;
}
}
}
悲观锁和乐观锁
悲观锁 : 同步操作就是 悲观锁
同步: 当一个线程操作的时候, 把所有其他线程拦在外面, 当前线程不操作完不允许其他线程干扰! -> 效率低
乐观锁 : CAS算法 + 自旋
CAS算法 + 自旋 : 不对共享数据上锁, 而是保留旧的预期值, 每次修改内存值之前去拿最新的内存值和预期值进行比较
相同 : 要修改的值赋值给内存值
不相同 : 重新获取最新内存值给预期值, 再次做修改
总结线程安全问题
多个线程操作共享数据, 极有可能产生线程安全的问题! !
解决方案:
1. 一定能解决的方案 : 同步操作-> 上锁 -- > 悲观锁
2. 如果是原子性操作的线程安全问题 : volatile 关键字
3. 如果是非原子性操作的线程安全问题 : Atomic 包下的原子性的类( CAS算法和自旋)
死锁
Object objA = new Object ( ) ;
Object objB = new Object ( ) ;
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
while ( true ) {
synchronized ( objA) {
synchronized ( objB) {
System . out. println ( "紫薇 不要走~" ) ;
}
}
}
}
} ) . start ( ) ;
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
while ( true ) {
synchronized ( objB) {
synchronized ( objA) {
System . out. println ( "尔康 我不想留~" ) ;
}
}
}
}
} ) . start ( ) ;
Hashtable和Vector
Vector 线程安全的元素可重复, 元素有索引, 元素存取有序的单列集合实现
如何保证线程安全呢? -- > 同步方法
Hashtable 线程安全的双列集合实现
如何保证线程安全呢? -- > 同步方法
ConcurrentHashMap<K,V>
ConcurrentHashMap < K , V > 替换了 Hashtable < K , V > , 原因是ConcurrentHashMap < K , V > 效率更高!
Hashtable 同步操作是把整个底层的哈希表都锁起来, 一旦有线程操作集合就不允许其他线程操作集合
ConcurrentHashMap < K , V > 同步操作是只锁住当前被线程操作的hash表单个索引位置, 只要其他的线程不操作被锁起来的hash表位置, 还是可以操作集合
ConcurrentHashMap < K , V > 底层结构在JDK7和JDK8中不同体现在Hash 表数据结构的实现方式不同:
1. JDK7 : 数组加链表
2. JDK8 : 数组加链表加红黑树
JDK7和JDK8的线程安全方式是一致的! !
CountDownLatch
CountDownLatch : 实现让一个线程执行的前提条件是, 其他线程都执行完毕后再执行
CountDownLatch 实现原理: 计数器
构造方法:
CountDownLatch ( int count)
成员方法:
void await ( ) : 休眠方法 -> 等到计数器归零后自己醒来
void countDown ( ) : 计数器- 1 的操作
案例:
package com. atguigu. h_countdownlatch ;
import java. util. concurrent. CountDownLatch ;
public class Demo {
public static void main ( String [ ] args) {
CountDownLatch latch = new CountDownLatch ( 3 ) ;
ChildThread1 t1 = new ChildThread1 ( latch, "大娃" ) ;
ChildThread2 t2 = new ChildThread2 ( latch, "二娃" ) ;
ChildThread3 t3 = new ChildThread3 ( latch, "三娃" ) ;
MotherThread motherThread = new MotherThread ( latch, "蛇精" ) ;
motherThread. start ( ) ;
t1. start ( ) ;
t2. start ( ) ;
t3. start ( ) ;
}
}
class ChildThread1 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread1 ( CountDownLatch countDownLatch, String name) {
super ( name) ;
this . countDownLatch = countDownLatch;
}
@Override
public void run ( ) {
for ( int i = 1 ; i <= 10 ; i++ ) {
System . out. println ( getName ( ) + "在吃第" + i + "个饺子" ) ;
}
countDownLatch. countDown ( ) ;
}
}
class ChildThread2 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread2 ( CountDownLatch countDownLatch, String name) {
super ( name) ;
this . countDownLatch = countDownLatch;
}
@Override
public void run ( ) {
for ( int i = 1 ; i <= 20 ; i++ ) {
System . out. println ( getName ( ) + "在吃第" + i + "个饺子" ) ;
}
countDownLatch. countDown ( ) ;
}
}
class ChildThread3 extends Thread {
private CountDownLatch countDownLatch;
public ChildThread3 ( CountDownLatch countDownLatch, String name) {
super ( name) ;
this . countDownLatch = countDownLatch;
}
@Override
public void run ( ) {
for ( int i = 1 ; i <= 15 ; i++ ) {
System . out. println ( getName ( ) + "在吃第" + i + "个饺子" ) ;
}
countDownLatch. countDown ( ) ;
}
}
class MotherThread extends Thread {
private CountDownLatch countDownLatch;
public MotherThread ( CountDownLatch countDownLatch, String name) {
super ( name) ;
this . countDownLatch = countDownLatch;
}
@Override
public void run ( ) {
try {
countDownLatch. await ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( "妈妈在收拾碗筷" ) ;
}
}
Semaphore
Semaphore 通行证的类
通行证 给线程对象颁发, 获取到通行证的线程对象就可以执行, 否则就不可以执行!
构造方法:
Semaphore ( int permits ) :
int permits : 通行证数量
成员方法:
void acquire ( ) : 获取通行证的方法
void release ( ) : 释放通行证的方法
super ( name) ;
this . countDownLatch = countDownLatch;
}
@Override
public void run ( ) {
try {
countDownLatch. await ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( "妈妈在收拾碗筷" ) ;
}
}