package listen;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class ThreadUnSee implements Runnable {
volatile int i = 0;
public static void main(String[] args) {
ThreadUnSee threadUnSee = new ThreadUnSee();
new Thread(threadUnSee).start();
for(int a=0;a<1000000;a++){
System.out.println(threadUnSee.i+"--"+a);
}
}
//无论是否使用volatile修饰,上面显示打印0,然后过了两秒钟后,run方法修改了i的值,马上返给主方法,此时打印100,再过两秒钟后,run再次修改i,然后又马上返给主方法,主方法打印0
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
i=100;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
i=0;
}
}
//无volatile修饰时,只能打印m start,无法打印m end
//使用volatile后,两句话都可以正常打印
package listen;
import java.util.concurrent.TimeUnit;
public class T {
volatile boolean running = true;
void m(){
System.out.println("m start");
while(running){
//此处一点代码没有,线程无法被重新调起,也就失去了被主内存重新推送新值的机会
//一旦添加System.out.println(a);代码,同时在主线程中写入循环修改running值,即使running不设置为volatile,也一定会获取主内存中的值,并成功结束循环
//没有volatile修饰,只不过结束的稍微晚一些
}
System.out.println("m end");
}
public static void main(String[] args) {
T t = new T();
new Thread(t::m,"123").start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.running=false;
}
}
package listen;
public class Mgr06 {
private static /*volatile*/ Mgr06 INSTANCE;
private Mgr06() {
}
//1. 单例的双重检查
public static Mgr06 getINSTANCE() {
//2. 为提高效率,不需同步的业务逻辑单独提炼出来
if (INSTANCE == null) {
//3. 不允许同时有两个线程访问,防止多线程导致单例失效
synchronized (Mgr06.class) {
//4. 两个线程可能同时访问到上方的==null代码,此时都是true,那么都会进入代码块,最后还是会创建出多个实例
//5. 为避免这种情况,再次判断INSTANCE是否为null
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//6. 但如果定义INSTANCE时,不加volatile,还是有可能导致问题产生
//7. volatile功能:禁止指令重排序
//8. 正常情况,应先为Mgr06对象先分配内存,然后为Mgr06中非静态成员变量赋初值,最后将栈中的INSTANCE引用,指向新创建出的这个对象
//8. 但超高并发时,cpu为了加快速度,可能对指令重新排序,先为Mgr06对象分配内存,然后直接将栈中的INSTANCE引用,指向新创建出的这个对象,此时Mgr06对象的非静态成员变量还没赋初值
//10. 此时如果正好第二个线程进来,判断INSTANCE并不是null,就直接将这个非静态成员还没正确初始化的对象拿到手
//11. 加上volatile,该对象上的指令重排序不再允许存在,一定会先初始化,再指向栈中变量
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(Mgr06.getINSTANCE().hashCode())).start();
}
}
}
package listen;
import java.util.ArrayList;
import java.util.List;
public class VolatileRepSyn {
volatile int count = 0;
void m() {
for (int i = 0; i < 10000; i++) count++;
}
public static void main(String[] args) {
VolatileRepSyn volatileRepSyn = new VolatileRepSyn();
List<Thread> threads = new ArrayList<>();
for(int i=0;i<10;i++){
//虽然定义了count为volatile,子线程一旦修改该值,就返回给主空间,而主空间又将返回后的值,通知给其他子线程
//但有可能出现同时将count值为100通知给了10个线程,他们都自加一次后,都将101写回,这样,虽然在10个线程中,count值都增加了1,但总值也才增加1
//必须使用synchronized void m(),来保证原子性
threads.add(new Thread(volatileRepSyn::m,"thread-"+i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try{
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(volatileRepSyn.count);
}
}
package listen;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class T01_AtomicInteger {
/*volatile int count = 0;*/
//替代了synchronized,自身带有原子性
AtomicInteger count = new AtomicInteger(0);
/*synchronized*/ void m() {
for (int i = 0; i < 10000; i++) count.incrementAndGet();
}
public static void main(String[] args) {
T01_AtomicInteger t = new T01_AtomicInteger();
List<Thread> threads = new ArrayList<>();
for(int i=0;i<10;i++){
threads.add(new Thread(t::m,"thread-"+i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try{
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count.get());
}
}
//cas算法伪代码
cas(原值,期望值,新值){
//表示cas操作之前,该值未被其他进程修改过
if(原值==期望值){
//无需担心CAS方法内部执行时,变量值被其他线程修改,因为CAS是CPU指令级操作,CPU保证其内不可被修改
原值==新值;
}else{
修改期望值,重新尝试
}
}
使用了分段锁的概念,分段锁也使用了CAS算法,它将所有线程分为几部分,分别对应不同的几个锁对象,最后将所有锁对象上的值加和得到最终结果
适用于线程数特别多的情况
可重入锁的概念:锁定一个对象后,这个线程,还可以对这个对象,再锁一次,如果不可重入,子类调用父类会有问题
package com.mashibing.juc.c_020;
import java.util.concurrent.CountDownLatch;
public class T06_TestCountDownLatch {
public static void main(String[] args) {
//CountDownLatch与join区别:更灵活,可以在一个线程中,就调用多次countDown方法,这样一个线程结束,就有可能使await阻塞结束
usingJoin();
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
//latch中计数减1
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
//一直等待latch中计数成0,否则一直阻塞
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end join");
}
}
package com.mashibing.juc.c_020;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class T07_TestCyclicBarrier {
public static void main(String[] args) {
//CyclicBarrier barrier = new CyclicBarrier(20);
//1. parties表示满多少人发车,barrierAction表示人满后启动一个新的线程
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("发车"));
//3. i为100时,打印5次发车,为99时,打印4次发车,同时最后剩余的19个线程,都等待一个新的线程进入
for (int i = 0; i < 99; i++) {
new Thread(() -> {
try {
//2. 阻塞线程,直到人满,并且人数+1
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
package com.mashibing.juc.c_020;
import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
public class T09_TestPhaser2 {
static Random r = new Random();
static MarriagePhaser phaser = new MarriagePhaser();
static void milliSleep(int milli) {
try {
TimeUnit.MILLISECONDS.sleep(milli);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//1. 设置初始阶段需要推倒栅栏的人数,也可以在new时就进行设置,该数值在上一个阶段结束后重置回到原值
phaser.bulkRegister(7);
for (int i = 0; i < 5; i++) {
new Thread(new Person("p" + i)).start();
}
new Thread(new Person("新郎")).start();
new Thread(new Person("新娘")).start();
}
static class MarriagePhaser extends Phaser {
//2. 当栅栏推倒时,自动调用该方法,phase表示第几个阶段,从0开始,每次增加1,registeredParties表示该阶段参与人数
//一旦该方法返回true,表示后面栅栏全部失效
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
System.out.println("所有人到齐了!" + registeredParties);
System.out.println();
return false;
case 1:
System.out.println("所有人吃完了!" + registeredParties);
System.out.println();
return false;
case 2:
System.out.println("所有人离开了!" + registeredParties);
System.out.println();
return false;
case 3:
System.out.println("婚礼结束!新郎新娘洞房!" + registeredParties);
return true;
default:
return true;
}
}
}
static class Person implements Runnable {
String name;
public Person(String name) {
this.name = name;
}
public void arrive() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 到达现场!\n", name);
//3. 先通知Phaser当前线程执行完毕,然后等待,直到本阶段需要的所有人到齐
phaser.arriveAndAwaitAdvance();
//4. arrive方法:只通知Phaser当前线程执行完毕,不等待
}
public void eat() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 吃完!\n", name);
phaser.arriveAndAwaitAdvance();
}
public void leave() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 离开!\n", name);
phaser.arriveAndAwaitAdvance();
}
private void hug() {
if (name.equals("新郎") || name.equals("新娘")) {
milliSleep(r.nextInt(1000));
System.out.printf("%s 洞房!\n", name);
phaser.arriveAndAwaitAdvance();
} else {
//5. 不阻塞,通知Phaser当前线程执行完毕,减少进入下一阶段总人数
phaser.arriveAndDeregister();
//6. 为当前阶段,增加一个参与者,该参与者在当前阶段相当于还没执行!
//phaser.register()
}
}
@Override
public void run() {
arrive();
eat();
leave();
hug();
}
}
}
package com.mashibing.juc.c_020;
import java.util.Random;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class T10_TestReadWriteLock {
static Lock lock = new ReentrantLock();
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over!");
//模拟读取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over!");
//模拟写操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//使用ReentrantLock时,读、写都无法并发执行,效率特别低
//Runnable readR = ()-> read(lock);
Runnable readR = ()-> read(readLock);
// Runnable writeR = ()->write(lock, new Random().nextInt());
Runnable writeR = ()->write(writeLock, new Random().nextInt());
//写时,加的是写锁(排它锁),即其他的读写都无法并行
for(int i=0; i<2; i++) new Thread(writeR).start();
//读时,加共享锁,读操作可以并行处理,写操作必须等待其前面执行的操作都结束
for(int i=0; i<18; i++) new Thread(readR).start();
}
}
//类似8车道,只有2个收费站
package com.mashibing.juc.c_020;
import java.util.concurrent.Semaphore;
public class T11_TestSemaphore {
public static void main(String[] args) {
//Semaphore s = new Semaphore(2);
//设置同时只允许2个线程执行
Semaphore s = new Semaphore(2, true);
new Thread(()->{
try {
//获取一次许可,允许的线程总数减少1。如果总许可数已为0,会阻塞
s.acquire();
System.out.println("T1 running...");
Thread.sleep(2000);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//允许的线程总数恢复1
s.release();
}
}).start();
new Thread(()->{
try {
s.acquire();
System.out.println("T2 running...");
Thread.sleep(2000);
System.out.println("T2 running...");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
package com.mashibing.juc.c_020;
import java.util.concurrent.Exchanger;
public class T12_TestExchanger {
//Exchanger相当于一个容器,该容器可以存储两个值
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(() -> {
String s = "T1";
try {
//该方法阻塞,向容器放入一个值
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "Thread-T1").start();
new Thread(() -> {
String s = "T2";
try {
//当第二个线程,执行到exchange,也扔入一个值,然后容器交换两个值的位置,并通知两个线程继续向前走
//无法处理三个线程,交换两个线程的局部变量数据
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "Thread-T2").start();
}
}
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class T13_TestLockSupport {
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
if(i == 5) {
//使线程阻塞
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 8 senconds!");
//叫醒指定的线程t。注意unpark可以优先于park执行,也不会报错
LockSupport.unpark(t);
}
}
package com.mashibing.juc.c_020_01_Interview;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class T01_WithoutVolatile {
List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
T01_WithoutVolatile c = new T01_WithoutVolatile();
new Thread(() -> {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i);
}
}, "t1").start();
new Thread(() -> {
while(true) {
//1. 不使用volatile时,可以加下面的打印,也能让t2结束,这是因为不加打印时,t2中使用c.size时候,不能特别及时看到t1中该值的改变,有可能t2跑到该段代码时,t2中的c.size还使用的之前的值4,然后就直接跳到了8,加打印可以增加t2中循环内的时间,增加了t1刷新缓存给主内存,而主内存又将新值分发给t2的机会
//2. 加上volatile后,能保证每次t2中的c.size都是最新值,但实际上,还有有可能,t1先执行,直接跑到了6,然后线程t2就永远无法结束循环,因此此处错误实际上并不是线程间是否可见导致的问题
//System.out.println(Thread.currentThread().getName()+" "+c.size());
//值得注意的是,volatile修饰的变量如果是对象或数,其含义是对象或数组的地址具有可见性,但是数组或对象内部的成员改变不具备可见性,但此处不能证明这点,因为volatile确实对size可见了,即使用volatile时,确实能做到让t2结束
if(c.size() == 5) {
break;
}
}
System.out.println("t2 结束");
}, "t2").start();
}
}
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
public class TestSelf {
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (lock){
System.out.println("t2启动");
try {
//5. wait虽然释放了锁,但它下面想继续执行,必须再次拿到锁,一旦有其他线程持续占着锁,那么wait就无法继续执行
//注意不能使用sleep,sleep不释放锁,wait释放锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2结束");
//4. 于此同时,由于t1还wait呢,必须由t2将其notify
// lock.notify();
}
}).start();
TimeUnit.SECONDS.sleep(5);
new Thread(()->{
synchronized (lock){
System.out.println("t1启动");
//1. 注意wait方法会释放锁,但notify不会,因此,一旦notify代码在加锁的代码块中,那么改段代码块必须执行完成,才能执行之前wait处代码
//2. 也就是,此处没法直接叫醒线程t2,必须先将t1执行完成
lock.notify();
//3. 加入下方代码,利用wait再次释放锁,就能够保证t2先执行完成
// try {
// lock.wait();
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("t1结束");
}
}).start();
}
}
package com.mashibing.juc.c_020_01_Interview;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class T07_LockSupport_WithoutSleep {
// 添加volatile,使t2能够得到通知
volatile List lists = new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
static Thread t1 = null, t2 = null;
public static void main(String[] args) {
T07_LockSupport_WithoutSleep c = new T07_LockSupport_WithoutSleep();
t1 = new Thread(() -> {
System.out.println("t1启动");
for (int i = 0; i < 10; i++) {
c.add(new Object());
System.out.println("add " + i);
if (c.size() == 5) {
LockSupport.unpark(t2);
LockSupport.park();
}
}
}, "t1");
t2 = new Thread(() -> {
//System.out.println("t2启动");
//if (c.size() != 5) {
LockSupport.park();
//}
System.out.println("t2 结束");
LockSupport.unpark(t1);
}, "t2");
t2.start();
t1.start();
}
}
package com.mashibing.juc.c_020;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyContainer2<T> {
final private LinkedList<T> lists = new LinkedList<>();
//容器中最多放十个元素
final private int MAX = 10;
//实时记录容器中元素个数
private int count = 0;
private Lock lock = new ReentrantLock();
// ReentranLock的Condition的原理
// 使用Condition时,相当于将等待队列变成了两个,一个producer,一个condition
// Condition的本质是等待队列。producer.await();相当于让当前线程在producer队列中进行等待
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t) {
try {
//必须加锁,因为防止两个线程判断==MAX时,都成功,导致同时向容器中放入值,且++count,导致容器中元素超量,count也超过MAX
lock.lock();
//此处必须使用while,因为如果使用if,那么一个生产者线程A发现容器满了之后,就进入等待,当他被唤醒,就会向容器内放入值
//但如果此时另一个成产者线程B,在他之前,已经向容器放入了元素,并导致容器再次变满,此时A并没有再次判断容器是否满,会的add方法放入第MAX+1个元素
while (lists.size() == MAX) {
producer.await();
}
lists.add(t);
++count;
//Synchronized中锁对象的wait和notify,做不到只唤醒消费者,这是ReentrantLock与Synchronized的不同点
consumer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get() {
T t = null;
try {
lock.lock();
while (lists.size() == 0) {
consumer.await();
}
t = lists.removeFirst();
count--;
producer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public int getCount(){
return count;
}
public static void main(String[] args) {
MyContainer2<String> c = new MyContainer2<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
System.out.println(c.get());
System.out.println("消费");
}
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 25; j++) {
c.put(Thread.currentThread().getName() + " " + j);
}
}, "p" + i).start();
}
}
}
变量句柄对象,代表指向某个变量的引用,那么创建对象时就有引用,为什么又要用一个引用指向该对象。是因为使用VarHandle引用,允许对普通属性进行原子操作,即拥有compareAndSet方法,而且比反射更快,直接操纵二进制码
package com.mashibing.juc.c_021_03_VarHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class T01_HelloVarHandle {
int x = 8;
private static VarHandle handle;
static {
try {
//使用VarHandle引用指向x
handle = MethodHandles.lookup().findVarHandle(T01_HelloVarHandle.class, "x", int.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
T01_HelloVarHandle t = new T01_HelloVarHandle();
//plain read / write
System.out.println((int)handle.get(t));
handle.set(t,9);
System.out.println(t.x);
handle.compareAndSet(t, 9, 10);
System.out.println(t.x);
handle.getAndAdd(t, 10);
System.out.println(t.x);
}
}
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
public class M {
//对象被垃圾回收后,会调用对象的finalize方法
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
}
}
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
import java.lang.ref.SoftReference;
public class T02_SoftReference {
public static void main(String[] args) {
//建立一个引用m,它指向一个软引用对象,这个软引用对象内部有一个被GC特殊处理的引用,该引用遇到内存不足时的垃圾回收,会被置为null,其引用的对象也就被回收掉了,这个引用才指向byte[]对象,这个对象占10M内存
//这种情况我们一般描述为建立一个软引用指向byte[]对象
SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
//这句话相当于将软引用对象都回收了,其内部的引用、以及引用所指向的byte[]对象自然也被回收
// m = null;
//get方法是获取软引用所指向的对象,此处为获取byte[]对象
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());
//再分配一个数组,总内存从10M升到25M,heap将装不下,只有这种防止内存溢出的垃圾回收,会将软引用中的引用置为null,从而回收掉其指向的对象
//修改启动时的JVM内存:Run--Edit Configurations--VM options-- -Xms20M -Xmx20M
//软引用是用来描述一些还有用但并非必须的对象。一般用做缓存,取的时候从缓存取,一旦有新对象进来,就可以把老的回收掉
byte[] b = new byte[1024*1024*15];
System.out.println(m.get());
}
}
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
import java.lang.ref.WeakReference;
public class T03_WeakReference {
public static void main(String[] args) {
//建立一个弱引用,指向M对象,弱引用一般用在容器中
WeakReference<M> m = new WeakReference<>(new M());
System.out.println(m.get());
//弱引用遭到gc就会回收
System.gc();
System.out.println(m.get());
}
}
/**
* 1. NIO中有一个DirectByteBuffer,直接内存,不被JVM直接管理,被操作系统管理,也叫做堆外内存
* 2. 这种对象压根没在堆里,即使将该对象所有引用设为null,垃圾回收器没法回收他
* 3. 可以使用虚引用指向堆外内存中的对象,当虚引用被垃圾回收器回收时,人为使用Unsafe类对堆外内存进行回收堆外内存,java11中的Unsafe无法直接访问了
*/
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;
public class T04_PhantomReference {
private static final List<Object> LIST = new LinkedList<>();
private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
public static void main(String[] args) {
//虚引用指向的对象,也是只要被gc就会被垃圾回收
//指定虚引用所指向对象M,和垃圾回收后,将该对象放入的ReferenceQueue类型的队列QUEUE
//弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用
PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);
new Thread(() -> {
while (true) {
//本例中需要将虚拟机内存调整为较小内存,方便触发垃圾回收
LIST.add(new byte[1024 * 1024]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
//虚引用永远也无法获取其引用的对象
System.out.println(phantomReference.get());
}
}).start();
new Thread(() -> {
while (true) {
Reference<? extends M> poll = QUEUE.poll();
//一旦队列中能取出值了,就说明虚引用所指向对象被垃圾回收了,并放入了该队列
if (poll != null) {
System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
}
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* ThreadLocal作用:Spring的声明式事务中,帮助每个方法都可以拿到同一个数据库连接
* 声明式事务:是建立在AOP之上的。本质是对方法前后进行拦截,然后在目标方法开始之前创建或者* 加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是* 不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置* 文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用* 到业务逻辑中
* 为了保证一系列的方法是一个完整的事务,那么必须每个人拿到的对象,必须是同一个connection
* 对象,connection对象一般都在连接池中,为保证每次拿到的都是同一个connection对象,可以* 将第一次取到的connection对象放到当前线程的ThreadLocal中,以后再拿都直接从*
* ThreadLocal拿,不从线程池中拿
* ThreadLocal线程局部变量
* 1. ThreadLocal的set方法,会向调用该set方法所在的线程T对应的t对象中的ThreadLocal.ThreadLocalMap类型(类似Map的结构)的成员变量threadLocals中的value设置值
* 2. ThreadLocal.ThreadLocalMap的key,是调用该set方法的ThreadLocal对象,value是set方法后面的参数对象
* 3. Thread.ThreadLocalMap
* 4. 也就是说,对于同一个线程,同一个ThreadLocal对象,只能放一个Object值
* 5. fcr为了对于同一个线程,同一个ThreadLocal放入多个值,就使用Map替换这个Object,这样就可以使用这个Map存放多组值,外表上看,也就是同一个线程,同一个ThreadLocal变量,可以放入多组值
* 6. ThreadLocal是使用空间换时间,synchronized是使用时间换空间
* 7. 比如在hibernate中session就存在与ThreadLocal中,避免synchronized的使用
*/
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
import java.util.concurrent.TimeUnit;
public class ThreadLocal2 {
//volatile static Person p = new Person();
static ThreadLocal<Person> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//注意ThreadLocal对象,实际上是有两个引用指向他的,一个是tl,一个是Thread中的ThreadLocalMap成员变量中的key
//tl是强引用,而key继承了弱引用
//1. Entry类继承弱引用的原因:可以方便的建立弱引用对象,让这个弱引用对象中的引用指向key对象,这样一来,当线程结束,tl就被指向null,这样就只有一个弱引用指向key对象,当再次垃圾回收,key对象就会被回收,从而防止内存泄露问题。如果不这样做,即使tl所在方法结束,由于key存在一个强引用指向,key对象永远不会被回收
//注意内存泄露不是内存溢出,内存够大就永远不会溢出
//2. 如果线程长期存在于线程池中,当key对象被回收,key值变为null,但其value由于有强引用指向,不会被回收,导致Map不会被回收,也导致ThreadLocal不会被回收,所以使用完ThreadLocal内存放的对象,必须remove掉
System.out.println(tl.get());
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tl.set(new Person());
}).start();
}
static class Person {
String name = "zhangsan";
}
}