public class StringPool58demo {
public static void main(String[] args) {
String s1 = new StringBuilder("j").append("ava").toString();
System.out.println(s1);
System.out.println(s1.intern());
System.out.println(s1 == s1.intern()); // false
String s2 = new StringBuilder("ali").append("baba").toString();
System.out.println(s2);
System.out.println(s2.intern());
System.out.println(s2 == s2.intern()); // true
}
}
sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化,而在初始化时他需要对静态常量字段根据指定常量值(ConstantValue)做默认初始化,此时被sun.misc.Version.launcher静态常量字段所引用的 “java” 字符创字面量就被intern到HotSpot VM的字符串常量池–StringTable里了
public static void main(String[] args) {
int[] nums = new int[]{2, 7, 11, 15};
int target = 9;
int[] ints = twoSum(nums, target);
System.out.println(Arrays.toString(ints));
}
public static int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int res = target - nums[i];
if (map.containsKey(res)) {
return new int[]{map.get(res), i};
}
map.put(nums[i], i);
}
return null;
}
/**
* 可重入锁 : 可重复递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁
*
* 在一个Synchronized 修饰的方法或代码块的内部
* 调用本类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的
*/
public class SyncCode {
public static void main(String[] args) {
Object objectA = new Object();
new Thread(()->{
synchronized (objectA){
System.out.println("outer");
synchronized (objectA){
System.out.println("middler");
synchronized (objectA){
System.out.println("inner");
}
}
}
},"t1").start();
}
}
public class SyncCode {
public synchronized static void m1( ) {
System.out.println("=== 外");
m2();
}
public synchronized static void m2( ) {
System.out.println("=== 中");
m3();
}
public synchronized static void m3( ) {
System.out.println("=== 内");
}
public static void main(String[] args) {
SyncCode.m1();
}
}
当执行monitorenter时,如果目标所对象的计数器为零,那么说明他没有被其他线程持有,java虚拟机会将该对象的持有线程设置为当前线程,并且将其计数器加1
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁
当执行monitorexit时,java虚拟机则需将锁计数器减1。计数器为零代表锁已释放
public class SyncCode {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try{
System.out.println("==外层");
lock.lock();
try{
System.out.println("==中层");
lock.lock();
try{
System.out.println("==内层");
} finally{
lock.unlock();
}
} finally{
lock.unlock();
}
}finally{
lock.unlock();
}
}
}
线程等待唤醒机制(wait/notify)
LockSupport中的park()和unpark()的作用分别是阻塞线程和解除阻塞线程
synchronized 或 lock 约束
LockSupport类中的park等待和unpark唤醒
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport类使用了一种名为Permit(许可)的概念来做阻塞和唤醒线程的功能,每个线程都有一个许可(permit)
permit只有两个值 1 和 0 ,默认是零
可以把许可看成(0,1)信号量(Semphore),但与Semaphore不同的是,许可的累加上线是1
public class SyncCode {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
}, "a");
a.start();
Thread b = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ " 去唤醒");
LockSupport.unpark(a);
}, "b");
b.start();
}
}
public class SyncCode {
public static void main(String[] args) {
Thread a = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
}, "a");
a.start();
Thread b = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+ " 去唤醒");
LockSupport.unpark(a);
}, "b");
b.start();
}
}
正常+无锁块要求
之前错误的先唤醒后调用,LockSupport照样支持
形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个
当调用park方法时
为什么可以先唤醒线程后阻塞线程?
因为unpark增加一个凭证,之后在调用park方法,就可以名正言顺的凭证消费,故不会阻塞
为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
因为凭证的数量最多为1,连续调用两侧unpark和调用一次unpark效果是一样的,只会增加一个凭证;而调用两次park去需要消费两个凭证,证不够,不能放行
是用来构建锁或者其他同步器组件的重量级基础框架及整个juc体系的基石,通过内置的FIFO 队列 来完成资源获取线程的排队工作,并通过一个 int类型的变量表示锁的持有状态
和AQS有关的
ReentrantLock
CountDownLatch
ReentrantReadWriteLock
Semaphore
…
锁,面向锁的使用者,定义了程序员和锁的交互的使用层API,隐藏了实现细节,你调用即可
同步器,面向锁的实现者,比如java并发大神DougLee,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等
如果共享资源被占用,就需要一定的阻塞等待唤醒机制保证锁分配,这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象表现。他将请求共享资源的线程封装成队列的结点(Node),通过CAS、自旋锁以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的控制效果