synchronized
线程安全的主要诱因
- 存在共享数据
- 存在多条线程共同操作这些共享数据
- 根本方法:同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作
互斥锁的特性
- 互斥性:同一时间只允许一个线程持有某个对象锁
- 可见性:锁被释放前,对共享变量所做的修改,对于随后获得该锁的线程是可见的
- synchronized锁的是对象而不是代码
获取的锁的分类:获取对象锁和获取类锁
获取对象锁的两种方法:
- 同步代码块,synchronized(this),synchronized(类实例对象)
- 同步非静态方法(synchronized method),锁的是当前对象的实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| public class SyncThread implements Runnable {
@Override public void run() { String threadName = Thread.currentThread().getName(); if (threadName.startsWith("A")) { async(); } else if (threadName.startsWith("B")) { syncObjectBlock1(); } else if (threadName.startsWith("C")) { syncObjectMethod1(); } else if (threadName.startsWith("D")) { syncClassBlock1(); } else if (threadName.startsWith("E")) { syncClassMethod1(); }
}
private void async() { try { System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } }
private void syncObjectBlock1() { System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (this) { try { System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
private synchronized void syncObjectMethod1() { System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } }
private void syncClassBlock1() { System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (SyncThread.class) { try { System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
private synchronized static void syncClassMethod1() { System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
|
对象锁和类锁的总结
synchronized底层实现原理
实现synchronized的基础
对象在内存中的布局(对象头,实例数据,对齐填充)
- Monitor 每个Java对象天生自带了一把看不见的锁,存在Java对象的对象头中
ObjectMonitor实现:
每个对象锁的线程会被封装成ObjectWaiter存入EntryList
synchronized历史
synchronized的四种状态
synchronized和reentrantLock
ReentrantLock(再入锁)
synchronized和reentrantLock公平性的设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class ReentrantLockDemo implements Runnable{ private static ReentrantLock lock = new ReentrantLock(false); @Override public void run(){ while (true){ try{ lock.lock(); System.out.println(Thread.currentThread().getName() + " get lock"); Thread.sleep(1000); } catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }
public static void main(String[] args) { ReentrantLockDemo rtld = new ReentrantLockDemo(); Thread thread1 = new Thread(rtld); Thread thread2 = new Thread(rtld); thread1.start(); thread2.start(); } } //输出交替
|
总结
JMM的内存可见性
java内存模型
内存模型规定了多线程程序读写操作规范
java内存模型(java memory model)描述的是一组规范,定义了程序中各个变量的访问方式
线程对变量的操作必须在本地内存中执行,线程先把变量从主内存拷贝到工作内存,然后对变量进行操作,再拷贝到主内存中,线程之间的通信必须通过主内存
JMM中的主内存
- 存储Java实例对象
- 包括成员变量,类信息,常量,静态变量等
- 属于数据共享的区域,多线程并发操作时会引发线程安全问题
JMM中的工作内存
- 储存当前方法的所有本地变量信息,#本地变量对其他线程不可见
- 字节码行号指示器,Native方法信息
- 数据线程私有数据区域,不存在线程安全问题
JMM与java内存划分是不同的概念层次
- JMM描述的是一组规则,围绕原子性,有序性,可见性展开
- 相似点:存在共享区域和私有区域
主内存与工作内存的数据存储类型以及操作方式归纳
方法里的基本数据类型本地变量将直接存储在工作内存的栈帧结构中
引用类型的本地变量:引用存储在工作内存中,实例存储在主内存中
成员变量,static变量,类信息均会被储存到主内存中
主内存共享的方式是线程各拷贝一份数据到工作内存中,操作完成后再刷新回主内存
JMM怎么解决可见性问题
指令重新排序需要满足的条件
A操作的结果对B操作可见,则A与B存在happens-before的关系
happens-before的概念
volatile:JVM提供的轻量级同步机制
- volatile修饰的变量对所有线程总是可见的
- 禁止指令重排
多线程操作可能引发线程安全=>
synchronized也具有可见性,可以省略volatile=>
volatile如何实现可见性
volatile如何禁止重排优化-(内存屏障,以及保证内存可见性)
单例是双重检测实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Singleton{ private volatile static Singleton instance;
private Singleton(){} public static Singleton getInstance(){ if(intstance==null){ instance=new Singleton(); }
if(instance==null){ synchronized(Singleton.class){ instance=new Singleton(); } }
if(instance==null){ synchronized(Singleton.class){ if(instance==null){ instance= new Singleton(); } } }
} }
|
CAS(Compare and Swap)
java线程池
利用Executors创建不同的线程池满足不同场景的需求
使用双端队列实现
为什么要使用线程池
Executor的框架
线程池的基本组成:
- corePoolSize:核心线程数量(长期驻留的线程数)
- maximumPoolSize:线程不够用时能创建的最大线程数
- workQueue:任务等待队列
- keepAliveTime:非核心线程等待时间
- threadFactory:创建新线程
- handler:线程池饱和策略(阻塞队列满了并且没有空闲线程)
线程池的状态
流程图
生命周期
线程池的大小如何选定