对象头的组成
// 观察对象的分代年龄
// 设置jvm的大小 --- -Xmx200m
package BingFaBianCheng.bingFaBianCheng4.test;
import BingFaBianCheng.bingFaBianCheng4.entity.A;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;
import java.util.ArrayList;
import java.util.List;
@Slf4j(topic = "enjoy")
public class TestJol {
static A l = new A();
static Thread t1;
static Thread t2;
static List<A> list = new ArrayList<A>();
public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
// // System.out.println(Integer.toHexString(l.hashCode()));
// log.debug("线程还未启动----无锁");
// //log.debug(ClassLayout.parseInstance(l).toPrintable());
//
//
t1 = new Thread(){
@SneakyThrows
@Override
public void run() {
while (true) {
Thread.sleep(200);
log.debug(ClassLayout.parseInstance(l).toPrintable());
}
}
};
t1.start();
for (int i = 0; i < 999999; i++) {
//Thread.sleep(2);
list.add(new A());
}
//
// t2 = new Thread(){
// @Override
// public void run() {
// testLock();
// }
// };
// t1.setName("t1");
//
// t1.start();
// //等待t1执行完后再启动t2
// t1.join();
//
// t2.setName("t2");
// t2.start();
}
/**
* synchronized 如果是同一个线程加锁
* 交替执行 轻量锁
* 资源竞争----mutex
*
*
*/
public static void testLock(){
//偏向锁 首选判断是否可偏向 判断是否偏向了 拿到当前的id 通过cas 设置到对象头
synchronized (l){
//t1 locked t2 ctlock
log.debug("name:"+Thread.currentThread().getName());
//有锁 是一把偏向锁
log.debug(ClassLayout.parseInstance(l).toPrintable());
}
}
}
没有进入内核态
只是一个CPU指令,不会unpark线程
并发执行时,造成余额不准
Sysnchronized锁住方法,可以实现并发
Atomic+cas(无锁机制):将余额定义为AtomicInteger
package BingFaBianCheng.bingFaBianCheng4.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 取钱操作
* cas
* result :0
*
*/
public class AccountCas implements Account {
public AccountCas(int balance){
this.balance=new AtomicInteger(balance);
}
private AtomicInteger balance;
@Override
public Integer query() {
return this.balance.get();
}
@Override
public void acquire(Integer i) {
while(true) {
//t1 prev=80
int prev = balance.get();
//t1 next 80 t2 80
int next = prev - i;
//t2 balance=80
if(balance.compareAndSet(prev, next)) {
break;
}
// 上面那一堆等价于下面一条指令
//balance.getAndAdd(-1*i);
}
}
}
cas方法保证操作的原子性,atomicInteger是volatile类型的,可以保证可见性
package BingFaBianCheng.bingFaBianCheng4.test;
/**
* sync是否为公平锁
*/
public class TestSysn {
//定义一把锁
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
//线程的数量
int N = 10;
Thread[] threads = new Thread[N];
for(int i = 0; i < N; ++i){
threads[i] = new Thread(new Runnable(){
public void run() {
/**
* 如果这里打印的结果是无序的则表示 非公平锁
* 有序则公平锁 --- 是否按照请求锁的顺序来拿锁
* 倒叙 为什么几乎上不可能研究
* 因为他存在一个队列 C++
*/
synchronized(lock){
//t0 1.6 mutext---t0 t1....t9 到一个队列当中的去阻塞(因为锁被main线程持有了)
System.out.println(Thread.currentThread().getName() + " get synch lock!");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
//main 线程可以得到锁 持有了锁
synchronized(lock){
for(int i = 0; i < N; ++i){
//t0
threads[i].start();
Thread.sleep(200);
}
}
//
// for(int i = 0; i < N; ++i)
// threads[i].join();
}
}
倒序打印
package BingFaBianCheng.bingFaBianCheng4.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
private static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
int N = 10;
Thread[] threads = new Thread[N];
for(int i = 0; i < N; ++i){
threads[i] = new Thread(new Runnable(){
public void run() {
//synchronized ()
/**
*
* 独占锁---顾名思义
* t1------t9全部在这里阻塞
* 非公平锁也是拿不到锁---阻塞---进入队列---
* 线程
*/
lock.lock();
System.out.println(Thread.currentThread().getName() + " lock!");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
});
}
//相当于synchronized ()
lock.lock();
for(int i = 0; i < N; ++i){
threads[i].start();
Thread.sleep(200);
}
lock.unlock();
for(int i = 0; i < N; ++i)
threads[i].join();
}
}
正序打印
jdk1.8是公平锁,打印出的线程是倒序输出的
jdk1.6之后是模拟mutex实现的
独占锁 — 只能被一个线程持有
默认是非公平锁,公平锁需要传入参数true,表示获取公平锁,不传或者传false皆为非公平锁。
1.所谓的公平锁和非公平锁他们首先会在加锁的时候去抢锁如果加锁失败。
2.他进入队列(线程还没有睡眠),这个时候不死心还会进行自旋再去获取锁
3.如果失败就睡眠
第一次加锁的时候 ,他不会去尝试加锁,他回去看一下我前面有没有人排队,如果有人排队,我则进入队列(并不等于排队)(线程没有睡眠),然后还不死心,再次看一下我有没有拿锁的资格,如果有继续拿,拿不到则睡眠(排队)
上面问题的答案是:一朝排队,永远排队,公平锁和非公平锁最大的区别就是一开始加锁的时候,非公平锁如果可以获取到锁,即使有其他线程在排队,也不会入队,而是直接获取锁成功,而公平锁不会直接获取锁,如果有其他线程在排队,会插入到队列的最末尾,等待被它的前置节点来唤醒
所以是顺序打印的结果
查看ReentrantLock的源码
获取锁的时候,就会执行hasQueuedPredecessors()方法,判断是否有人排队
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
ReentrantLock第一次加锁,t1来加锁,队列都没有初始化,不需要排队,此时获取锁的效率最高
t2来加锁
如果t1和t2是交替执行,那么队列永远没有初始化,锁的获取永远都是最高效的状态
如果t1没有释放锁,发生了资源竞争,会初始化队列,队列中只有一个节点,所以也不需要排队,会去获取锁,如果获取锁失败,然后再入队,
t3来加锁,获取锁失败,直接进入addWaiter()方法,
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
如果队列没有初始化的话,又会进入enq()方法
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}