1、valotile关键字
1、保证可见性
2、不保证原子性
3、禁止指令重排
2、intern()方法
String:intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
考查点 - intern()方法,判断true/false?- 《深入理解java虚拟机》书原题是否读过经典JVM书籍
这段代码在JDK 6中运行,会得到两个false,而在JDK 7中运行,会得到一个true和一个false。产生差异的原因是,在JDK 6中,intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果将返回false。
而JDK 7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false,这是因为“java”这个字符串在执行StringBuilder.toString()之前就已经出现过了,字符串常量池中已经有它的引用,不符合intern()方法要求“首次遇到"”的原则,“计算机软件"这个字符串则是首次出现的,因此结果返回true。
https://blog.youkuaiyun.com/u011863024/article/details/115270840
3、可重入锁理论
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。
Java中ReentrantLock 和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
隐式锁(即synchronized关键字使用的锁)默认是可重入锁。
- 同步块
- 同步方法
synchronized是可重入锁的验证:
public class SynchronizedDemo2 {
public static void main(String[] args) {
new ReentrantLockDemo2().m1();
}
public synchronized void m1() {
System.out.println("===外");
m2();
}
public synchronized void m2() {
System.out.println("===中");
m3();
}
public synchronized void m3() {
System.out.println("===内");
}
}
输出结果:
===外
===中
===内
Synchronized的重入的实现机理
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
ReentrantLock是可重入锁的验证:
- 显式锁(即Lock)也有ReentrantLock这样的可重入锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Phone2 implements Runnable{
Lock lock = new ReentrantLock();
/**
* set进去的时候,就加锁,调用set方法的时候,能否访问另外一个加锁的set方法
*/
public void getLock() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t get Lock");
setLock();
} finally {
lock.unlock();
}
}
public void setLock() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t set Lock");
} finally {
lock.unlock();
}
}
@Override
public void run() {
getLock();
}
}
public class ReentrantLockDemo {
public static void main(String[] args) {
Phone2 phone = new Phone2();
/**
* 因为Phone实现了Runnable接口
*/
Thread t3 = new Thread(phone, "t3");
Thread t4 = new Thread(phone, "t4");
t3.start();
t4.start();
}
}
输出结果:
t3 get Lock
t3 set Lock
t4 get Lock
t4 set Lock
4、LockSupport是什么
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport中的park()和 unpark()的作用分别是阻塞线程和解除阻塞线程。
总之,比wait/notify,await/signal更强。
3种让线程等待和唤醒的方法
- 方式1:使用Object中的wait()方法让线程等待,使用object中的notify()方法唤醒线程
- 方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程
- 方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
重点说明
LockSupport是用来创建锁和共他同步类的基本线程阻塞原语。
LockSuport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻寨之后也有对应的唤醒方法。归根结底,LockSupport调用的Unsafe中的native代码。
LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,
调用一次unpark就加1变成1,
调用一次park会消费permit,也就是将1变成0,同时park立即返回。
如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。
形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。
当调用park方法时
如果有凭证,则会直接消耗掉这个凭证然后正常退出。
如果无凭证,就必须阻塞等待凭证可用。
而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无放。
面试题
为什么可以先唤醒线程后阻塞线程?
因为unpark获得了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。
为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
因为凭证的数量最多为1(不能累加),连续调用两次 unpark和调用一次 unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证,证不够,不能放行。
5、AQS理论
是什么?AbstractQueuedSynchronizer 抽象队列同步器。
6、根据前端传参区别不同维度分组
<select id="productCoinCount" resultMap="coinManageVO" parameterType="java.util.Map">
select
<if test="type==null">to_char(date(data_date),'yyyyMMdd')</if>
<if test="type==1">to_char(date(data_date),'yyyyMMdd')</if>
<if test="type==2">to_char(date(data_date),'yyyyMM')</if>
<if test="type==3">to_char(date(data_date),'yyyy')</if>
AS data_date,currency,reward_owner,
coalesce(sum(fact_reward),0) AS fact_reward, -- 实际产量,
coalesce(sum(all_net_show_reward),0) AS all_net_show_reward, -- 全网应产量,
coalesce(sum(fact_reward) - sum(all_net_show_reward),0) AS diff_reward
from rep.ads_bw_allocate_coin_report
<where>
<if test="currency!=null">
and currency = #{currency,jdbcType=VARCHAR}
</if>
<if test="startDate !=null">
and data_date >= #{startDate,jdbcType=VARCHAR}
</if>
<if test="endDate !=null">
and data_date <= #{endDate,jdbcType=VARCHAR}
</if>
</where>
group by 1,2,3
order by 1 ASC
</select>
详细请看:https://blog.youkuaiyun.com/u011863024/article/details/114684428