1.减少锁持有时间
2.减少锁粒度
3.读写锁替代独占锁
4.锁分离
5.锁粗化
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly(); //take锁加锁
try {
while (count.get() == 0) {
notEmpty.await(); //如果无可用数据则一直等待,知道put()方法的通知
}
x = dequeue();
c = count.getAndDecrement(); //原子操作-1
if (c > 1)
notEmpty.signal(); //通知其他take方法
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull(); //通知put方法 已有空间
return x;
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node node = new Node(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
for(int i=0;i<100;i++){
synchronized(lock){ //此时把锁放到循环外边去最好
//...
}
}
1.锁偏向
2.轻量级锁
3.自旋锁
4.锁消除
线程的局部变量,仅当前线程可以访问,故实现线程安全.
1.实现原理
2.ThreadLocal类的get, set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //获取threadlocalmap
if (map != null)
map.set(this, value); //键位当前threadlocal对象,value为保存值
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
回收清理:
线程类Thread在退出时,会进行一些清理工作,其中包括清理ThreadLocalMap
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null; //置空
inheritableThreadLocals = null; //置空
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
注意:
使用线程池未必会清除threadlocal,因为线程总会存在,而不会退出,多次使用后可能造成内存泄漏.故此处推荐使用ThreadLocal.remove()方法来移除变量.
ThreadLocalMap实现了弱引用,即进行GC时,一旦发现弱引用则立即回收.ThreadLocalMap中每一个Entry是一个WeakReference,ThreadLocal为key. 当ThreadLocal的外部强引用被回收时,ThreadLocalMap的key将会变成null.放系统进行ThreadLocalMap清理时(添加新变量进入表中时,会自动进行清理),垃圾数据会被回收.
无锁属于乐观锁,不采用重量级锁,主要实现原理为CAS比较交换.
CAS(V,E,N) V表示要更新的变量,E表示预期值,N表示新值.当V=E时才会去更新V=N,否则不作任何操作,多个线程去CAS更新时,只会有一个成功
当存在这样一个场景时,AtomicReference便无效了:
//有一家蛋糕店,当客户余额低于20元时,主动给客户赠送20元余额,每位客户只能被赠送一次
//如果使用原子类的话,当客户低于20元,获赠20元余额时,客户再次消费20元整数额,此时,赠予前和赠予后金额一致,故可能存在多次赠予的情况
public class AtomicReferenceDemo {
static AtomicReference money=new AtomicReference();
public static void main(String[] args) {
money.set(19);
//模拟多个线程同时更新后台数据库,为用户充值
for(int i = 0 ; i < 3 ; i++) {
new Thread() {
public void run() {
while(true){
while(true){
Integer m=money.get();
if(m<20){
if(money.compareAndSet(m, m+20)){
System.out.println("余额小于20元,充值成功,余额:"+money.get()+"元");
break;
}
}else{
//System.out.println("余额大于20元,无需充值");
break ;
}
}
}
}
}.start();
}
//用户消费线程,模拟消费行为
new Thread() {
public void run() {
for(int i=0;i<100;i++){
while(true){
Integer m=money.get();
if(m>10){
System.out.println("大于10元");
if(money.compareAndSet(m, m-10)){
System.out.println("成功消费10元,余额:"+money.get());
break;
}
}else{
System.out.println("没有足够的金额");
break;
}
}
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
}
}.start();
}
}
public class AtomicStampedReferenceDemo {
static AtomicStampedReference money=new AtomicStampedReference(19,0);
public static void main(String[] args) {
//模拟多个线程同时更新后台数据库,为用户充值
for(int i = 0 ; i < 3 ; i++) {
final int timestamp=money.getStamp();
new Thread() {
public void run() {
while(true){
while(true){
Integer m=money.getReference();
if(m<20){
if(money.compareAndSet(m, m+20,timestamp,timestamp+1)){ //CAS修改值,并且修改时间戳
System.out.println("余额小于20元,充值成功,余额:"+money.getReference()+"元");
break;
}
}else{
//System.out.println("余额大于20元,无需充值");
break ;
}
}
}
}
}.start();
}
//用户消费线程,模拟消费行为
new Thread() {
public void run() {
for(int i=0;i<100;i++){
while(true){
int timestamp=money.getStamp();
Integer m=money.getReference();
if(m>10){
System.out.println("大于10元");
if(money.compareAndSet(m, m-10,timestamp,timestamp+1)){
System.out.println("成功消费10元,余额:"+money.getReference());
break;
}
}else{
System.out.println("没有足够的金额");
break;
}
}
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
}
}.start();
}
}
public class AtomicIntegerArrayDemo {
static AtomicIntegerArray arr = new AtomicIntegerArray(10);
public static class AddThread implements Runnable{
public void run(){
for(int k=0;k<10000;k++)
arr.getAndIncrement(k%arr.length()); //k%length索引处+1
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] ts=new Thread[10];
for(int k=0;k<10;k++){
ts[k]=new Thread(new AddThread());
}
for(int k=0;k<10;k++){ts[k].start();}
for(int k=0;k<10;k++){ts[k].join();}
System.out.println(arr);
}
}
public class AtomicIntegerFieldUpdaterDemo {
public static class Candidate{
int id;
volatile int score; //不支持静态变量,且必须申明为volatile,且访问权限要支持(private不行)
}
public final static AtomicIntegerFieldUpdater scoreUpdater
= AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score"); //声明要升级的类以及成员变量.
//检查Updater是否工作正确
public static AtomicInteger allScore=new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Candidate stu=new Candidate();
Thread[] t=new Thread[10000];
for(int i = 0 ; i < 10000 ; i++) {
t[i]=new Thread() {
public void run() {
if(Math.random()>0.4){
scoreUpdater.incrementAndGet(stu); //使用升级类去做CAS原子自增
allScore.incrementAndGet(); //验证升级类是否正确
}
}
};
t[i].start();
}
for(int i = 0 ; i < 10000 ; i++) { t[i].join();}
System.out.println("score="+stu.score); //最终打印 score和allscore一致.
System.out.println("allScore="+allScore);
}
}
注意:
1.只能修改可见范围内的变量,因为Updater使用反射得到变量.
2.为了保证变量被正确读取,必须为volatile
3.CAS通过对象实例中的偏移量进行复制,因此,不支持static变量
Object transfer(Object e,boolean timed,long nanos) //e不为空时,表示入队,e为空时表示出队
//timed 表示是否有超时,nanos表示超时时间
//返回值为空表示失败,不为空表示成功
SynchronousQueue内部会维护一个线程等待队列,保存等待线程及相关数据信息,如生产者将数据放入SynchronousQueue中,如果没有消费者接受,则数据本身和对象都会打包在队列中等待(SynchronousQueue本身容量为0,数据是不能放入的,但是内部的线程等待队列可以存放)
Transferer.transfer()函数的实现是SynchronousQueue的核心,大提分为三个步骤.
(1)如果等待队列为空,或者队列中节点的类型和本次操作是一致的,那么将当前操作压入队列等待。比如,等待队列中是读线程等待,本次操作也是读,因此这两 个读都需要等待。进入等待队列的线程可能会被挂起,它们会等待一个“匹配” 操作
(2)如果等待队列中的元素和本次操作是互补的(比如等待操作是读,而本次操作是写),那么就插入一个“完成”状态的节点,并且让它“匹配”到一个等待节点 上。接着弹出这两个节点,并且使得对应的两个线程继续执行。
(3)如果线程发现等待队列的节点就是“完成”节点,那么帮助这个节点完成任务,其流程和步骤(2)是一致的。
定位:
jps:查看java进程的进程ID
jstack (ID):查看线程的线程堆栈
为了避免死锁问题,可以使用无锁函数以及重入锁,通过重入锁的中断或者限时等待可以有效规避死锁带来的问题
喜欢可以关注我啊~ 小编非常努力的在更新了QAQ~