synchronized锁机制
synchronized的执行过程:
检测Mark Word里面是否已存在某一个线程ID,若Mark Word里面不存在某一个线程ID,则CAS将当前线程的ID替换Mark Word,如果成功则表示当前线程获得了偏向锁,可执行同步代码块;如果失败,则说明发生竞争,跳转到步骤3。
若Mark Word里面已存在某一个线程ID,且为当前线程的ID,表示当前线程已获取了偏向锁,无需进行CAS操作来加锁解锁,直接执行同步代码块即可。
若Mark Word里面已存在某一个线程ID,但不是当前线程ID,则说明发生竞争,跳转到步骤3。
发生竞争,此时先撤销偏向锁,然后升级为轻量级锁。
升级为轻量级锁的过程是这样的:线程在执行同步代码块之前,JVM先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中。官方称之为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,则当前线程获得轻量级锁。
如果失败,表示其他线程竞争锁,则当前线程尝试使用自旋来获取锁。
如果自旋失败 ...
ThreadLocal线程变量
Thread类里面有一个threadLocals成员变量
ThreadLocal类下面有一个静态内部类ThreadLocalMap
ThreadLocal类下有get,set,remove方法
ThreadLocal使用:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778public class Solution { // 初始化方式一,重写 private static ThreadLocal<String> threadLocal_1 = new ThreadLocal<>() { @Override public String initialValue() { return " ...
java.lang.Thread类
该类实现了Runnable接口
线程状态一般可分为如下几种
创建(new),就绪(runnable),运行状态(running),阻塞(blocked),time waiting或waiting,消亡(dead)。
超时等待(time waiting)状态,超过限制时间后,该线程将返回到就绪(runnable)状态。
Thread类中常用的方法:
start()方法
start方法用来启动一个线程,当调用start()方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
run()方法
run()方法不需要用户来调用,start方法启动一个线程之后,当该线程获得CPU执行时间时,便会自动进入run方法内去执行具体任务。Thread类必须要重写run方法(Runnable接口下的虚方法)。
sleep方法
12sleep(long millis); // 参数为毫秒sleep(long millis, int nanoseconds); // 第一参数为毫秒,第二参数为纳秒
sleep让线程睡眠,交出C ...
jdk1.8的杂货
map遍历
1234567891011121314151617181920212223242526272829Map<String, Integer> map = new HashMap<>();map.put("a", 1);map.put("b", 2);map.put("c", 3);for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("key: " + entry.getKey()); Syetem.out.println("value: " + entry.getValue());}for (String key : map.keySet()) { System.out.println("key: " + key);}for (Integer value : ma ...
ArrayList和LinkedList的区别
ArrayList和LinkedList的区别
底层数据结构不同,前者底层基于数组实现,后者底层基于链表实现;
前者更适合指定参数查找,后者更适合删除添加;
两者都实现了List接口,后者额外实现了Deque接口,还可用作队列使用;
jdk1.6时LinkedList是双向循环链表,jdk1.7后为简单的双向链表;
LinkedList的get查找时,先校验index的有效性,再比较index和整体size,index较小则从first开始遍历,否则从last开始遍历;
LinkedList删除方法remove,默认删除的是首个元素;
参考链接
HashMap
HashMap特性
HashMap存储键值对实现快速存取,key不可重复,重复则覆盖。HashMap对象的key、value均可为null;HashTable对象的key、value均不可为null。
线程不安全
具体表现1:put的时候多线程导致数据不一致。假定线程A、B,其中A希望插入一个key-value对到HashMap,首先记录所要落到的桶的索引坐标,然后获得该桶里面的链表头节点,此时线程A的时间片用完。此时线程B开始执行,和线程A一样,只不过B成功将记录插到了桶里面。假设A计算出的桶索引与B计算出的桶索引一样,那么B插入成功之后,A再被调度执行并插入,那么B所插入额记录就会被A凭空覆盖。参考链接
具体表现2:jdk1.7的扩容机制resize采用头插法,新table中链表的顺序和旧链表的顺序是相反的,这种头插法在多线程时可能会导致环状节点。参考链接1,参考链接2
而jdk1.8的扩容机制resize采用尾插法,无环状节点问题。(但是1.8仍存在表现1的问题)
(扩容是指HashMap的整体容量要变大,不是只扩某一个桶链表的长度)
底层Hash表,不 ...
Java锁的内存语义
123456789101112class MonitorExample { int a = 0; public synchronized void writer() { ++a; } public synchronized void reader() { int i = a; // ... }}// 假设线程A执行writer()方法,线程B执行reader()方法
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主存中。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主存中读取共享变量。
对比锁释放-获取的内存语义与volatile写-读的内存语义可以看出:释放锁与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。
即总结如下:
线程A释放一个锁,实质上是线程A向接下来要获取这个锁的某个线程做出了(线程A对共享变量所做修改的)消息。
线程B获取一个锁,实质 ...
Spring IOC
控制反转 Inversion of Control
Spring中每一个需要管理的对象称为Spring Bean(简称Bean),而Spring管理这些Bean的容器,我们称之为Spring IOC容器。
IOC容器两个基本功能:管理Bean;完成Bean之间的依赖关系。
所有的IOC容器都需要实现BeanFactory接口(顶级接口),因其功能不够强大,还涉及了其子接口即ApplicationContext接口。现实中大部分IOC容器是ApplicationContext接口的实现类,即基于注解的IOC容器,AnnotationConfigApplicationContext
装配Bean
1、一个个装配,麻烦
1234567891011121314151617181920212223242526/*POJO*/public class User { private Long id; private String useName; private String note; /*setter and getter*/} ...
synchronized关键字的使用
类锁与对象锁的区别
类锁所有对象一把锁
对象锁一个对象一把锁,多个对象多把锁
同步是对同一把锁而言的,同步这个概念是在多个线程争夺同一把锁的时候才能实现的,如果多个线程争夺不同的锁,那多个线程是不能同步的。
两个线程一个取对象锁,一个取类锁,则不能同步
两个线程一个取a对象锁,一个取b对象锁,则不能同步
synchronized实现同步的原因在于,保证了拿到锁的线程一定可以一次性把他调用的方法或代码块执行完。
synchronized用于方法:
1234567891011121314public class B { // 修饰静态方法,或者修饰普通方法 synchronized public static void mB(String value) throws InterruptedException { for(int i = 0; i < 1000; ++i) { System.out.println(value); } } ...
Java内存模型补充
在Java中,所有实例域,静态域,数组元素,都存储在堆内存中,堆内存在线程之间共享。
局部变量,方法定义参数,异常处理器参数,不会在线程之间共享,它们不会有内存可见性的问题,也不受内存模型的影响,是每一个线程私有的,不会被共享,自然也就不会存在竞争问题。(Java并发编程的艺术22页,深入理解JVM 441页)
(我们讨论Java内存模型的时候,讨论主内存和线程本地内存的数据共享问题的时候,考虑的是所有线程的全局变量,是不考虑每一个线程独有的局部变量的。每一个线程独有的局部变量,不涉及共享问题。)
(Java并发编程的艺术,22页图3-1,其中的“本地内存A”,也是只考虑所有线程的全局变量,是不考虑每一个线程独有的局部变量。)
此处请读者注意区分概念:如果局部变量是一个reference类型,它引用的对象在Java堆中可被各个线程共享,但是reference本身在Java栈的局部变量表中是线程私有的。
Java所有的对象实例以及数组几乎都在堆上分配。
ThreadLocal线程局部变量,针对的是类成员变量,正常应线程共享该成员变量,但是ThreadLocal使得每个线程都有一个该成员 ...