JUC并发包
约 5861 字大约 20 分钟
(一) 线程基础
1. 购票问题
public class Thread01 {
public static void main(String[] args) {
Ticket01 ticket = new Ticket01();
new Thread(()->ticket.sale(), "A").start();
new Thread(()->ticket.sale(), "B").start();
}
}
class Ticket01 {
int number = 1;
public void sale() {
if (number > 0) {
if (Thread.currentThread().getName().equals("A")) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + "买票,现有" + number-- +",剩余" + number);
}
}
}
2. 购票解决
public class Thread02 {
public static void main(String[] args) {
Ticket02 ticket = new Ticket02();
new Thread(()->ticket.sale(), "A").start();
new Thread(()->ticket.sale(), "B").start();
}
}
class Ticket02 {
int number = 1;
public synchronized void sale() {
if (number > 0) {
if (Thread.currentThread().getName().equals("A")) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + "买票,现有" + number-- +",剩余" + number);
}
}
}
3. 生产消费
public class Thread03 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()-> {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
data.m1();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "A").start();
new Thread(()-> {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
data.m2();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "B").start();
}
}
// 说明:生产者、消费者、线程之间通信
// 注意:只有synchronized同步方法才可以调用wait、notifyAll
class Data {
int number = 0;
public synchronized void m1() throws InterruptedException {
if (number == 1) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "生产完成");
this.notifyAll();
}
public synchronized void m2() throws InterruptedException {
if (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "消费完成");
this.notifyAll();
}
}
4. Callable实现
/*
等价关系,new Runable等价于Runable的实现类,Callable<V> callable等价于Callable实现类对象
new Thread(new Runable()).start;
new Thread(new FutureTask()).start;
new Thread(new FutureTask(Callable<V> callable);
线程的创建方式主要有Thread、Runnable、Callable这几种,那么他们的主要区别是:
1.是否返回值?
Runnable无返回值,而Callable有返回值。
2.是否跑出异常?
Runnable无异常,Callable有异常
3.方法不同?
Runnable 运行的方法是run,Callable运行的方法是call
Callable有什么优点
缓存: 结果缓存!效率提高N倍
结果获取会阻塞:task.get() 获取值的方法一般放到最后,保证程序平稳运行的效率,因为他会阻塞等待结果产生!
*/
public class MyCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// Callable实现多线程
// Runnable线程接口:不返回结果、run()方法
// Callable线程接口:可以有返回值、可以抛出异常、call()方法、需要创建FutureTask、它将在运行时执行给定的Callable
MyThreadd myThread = new MyThreadd();
// Thread与Callable之间的适配类:FutureTask
// 在设计模式中建立两者之间的关系经常使用适配器。什么是适配器呢?
//适配器就是接口转换器。把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
FutureTask futureTask = new FutureTask(myThread);
// 注意一:有缓存、会打印1个call
new Thread(futureTask, "A").start();;//启动callable
new Thread(futureTask, "B").start();//结果会被缓存,效率高
new Thread(futureTask, "C").start();//结果会被缓存,效率高
// 注意二:这个get方法结果可能需要等待会产生阻塞、或者使用异步通信来处理
Integer i = (Integer) futureTask.get();//获取Callable的返回结果,get()方法可能会产生阻塞!
System.out.println(i);
}
}
class MyThreadd implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call");
TimeUnit.SECONDS.sleep(3);
return 1024;
}
}
5. Callable案例
public class MyCallable2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// SpringMVC模拟效果!
// (1)单线程
// long a = System.currentTimeMillis();
// Service1 s1 = new Service1();
// Service2 s2 = new Service2();
// Service3 s3 = new Service3();
// System.out.println(s1.m1());
// System.out.println(s2.m2());
// System.out.println(s3.m3());
// System.out.println(System.currentTimeMillis() - a);
// (2)多线程
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
Service1 s1 = new Service1();
return s1.m1();
}
};
FutureTask<String> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
String s = futureTask.get();// 主线程会大量阻塞、等待到有结果出来
System.out.println(s);
Callable callable2 = new Callable() {
@Override
public Object call() throws Exception {
Service2 s2 = new Service2();
return s2.m2();
}
};
FutureTask<String> futureTask2 = new FutureTask<>(callable2);
new Thread(futureTask2).start();
String s2 = futureTask2.get();// 主线程会大量阻塞、等待到有结果出来
System.out.println(s2);
Callable callable3 = new Callable() {
@Override
public Object call() throws Exception {
Service3 s3 = new Service3();
return s3.m3();
}
};
FutureTask<String> futureTask3 = new FutureTask<>(callable3);
new Thread(futureTask3).start();
String s3 = futureTask3.get();// 主线程会大量阻塞、等待到有结果出来
System.out.println(s3);
}
}
class Service1 {
String m1() {
return "查询用户信息";
}
}
class Service2 {
String m2() {
return "查询余额信息";
}
}
class Service3 {
String m3() {
return "查询积分信息";
}
}
(二) Lock锁
1. Lock
/**
* Synchronized和Lock比较
* Synchronized自动释放锁、可重入锁非公平
* Lock必须手动释放、如果不释放会死锁、可重入锁非公平可以设置更改
*/
public class Thread04 {
public static void main(String[] args) {
Ticket04 ticket = new Ticket04();
new Thread(()->ticket.sale(), "A").start();
new Thread(()->ticket.sale(), "B").start();
}
}
class Ticket04 {
int number = 1;
// ReentrantLock源码:
// sync = new NonfairSync();默认非公平锁
// FairSync公平锁先来后到、NonfairSync非公平锁可以插队
// public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (number > 0) {
if (Thread.currentThread().getName().equals("A")) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + "买票,现有" + number-- + ",剩余" + number);
}
} finally {
lock.unlock();
}
}
}
2. Condition
public class Thread05 {
public static void main(String[] args) {
Data05 data = new Data05();
new Thread(()-> {
while (true) {
data.m1();
}
}, "A").start();
new Thread(()-> {
while (true) {
data.m2();
}
}, "B").start();
}
}
// 说明:生产者、消费者、线程之间通信
// 注意:Condition是依赖Lock对象的。Condition在调用方法之前必须获取锁。
class Data05 {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
int number = 0;
public void m1() {
lock.lock();
try {
if (number == 1) {
condition.await(); // 等待
}
number++;
System.out.println(Thread.currentThread().getName() + "生产完成");
condition.signalAll(); // 唤醒全部
} catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m2() {
lock.lock();
try {
if (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "生产完成");
condition.signal();
} catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3. Condition多个
public class Thread06 {
public static void main(String[] args) {
Data06 data = new Data06();
new Thread(()-> {
while (true) {
data.printA();
}
}, "A").start();
new Thread(()-> {
while (true) {
data.printB();
}
}, "B").start();
new Thread(()-> {
while (true) {
data.printC();
}
}, "C").start();
}
}
class Data06 {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
int number = 1; // 1A 2B 3C
public void printA() {
lock.lock();
try {
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAA");
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCC");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
(三) 集合线程
1. CopyOnWriteArrayList
public class Thread07 {
public static void main(String[] args) {
// 并发修改异常ConcurrentModificationException
// List list = new ArrayList();
// 写入时复制、多个线程调用的时候、在写入的时候避免覆盖、造成数据丢失、读写分离
List list = new CopyOnWriteArrayList();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(list);
},String.valueOf(i)).start();
}
// 底层源码:
// public boolean add(E e) {
// final ReentrantLock lock = this.lock;
// lock.lock();
// try {
// Object[] elements = getArray();
// int len = elements.length;
// Object[] newElements = Arrays.copyOf(elements, len + 1);
// newElements[len] = e;
// setArray(newElements);
// return true;
// } finally {
// lock.unlock();
// }
// }
}
}
2. CopyOnWriteArraySet
public class Thread07 {
public static void main(String[] args) {
Set set = new CopyOnWriteArraySet();
for (int i = 0; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(set);
},String.valueOf(i)).start();
}
// Map map = new HashMap(16, 0.75F);
Map map = new ConcurrentHashMap();
for (int i = 0; i < 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,3));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
3. ConcurrentHashMap
public class Thread07 {
public static void main(String[] args) {
// ConcurrentHashMap底层源码原理:
// 线程安全实现方式:
// JDK 1.7 采用 Segment 分段锁来保证安全, Segment 是继承自 ReentrantLock。
// JDK1.8 放弃了 Segment 分段锁的设计,采用 Node + CAS + synchronized 保证线程安全,锁粒度更细,synchronized 只锁定当前链表或红黑二叉树的首节点。
// Hash 碰撞解决方法 :
// JDK 1.7 采用拉链法,
// JDK1.8 采用拉链法结合红黑树(链表长度超过一定阈值时,将链表转换为红黑树)。
//
// 并发度:
// JDK 1.7 最大并发度是 Segment 的个数,默认是 16。
// JDK 1.8 最大并发度是 Node 数组的大小,并发度更大。# Collection
}
}
(四) 读写锁
1. ReentrantReadWriteLock
/**
* 独占锁(写锁)一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* 读写锁规则:
* 读读 可以共存
* 读写 不能共存
* 写写 不能共存
*/
public class Thread8 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//MyCacheLock myCache = new MyCacheLock();
// 写
for (int i = 1; i <= 5; i++) {
final int t = i;
new Thread(()->{
myCache.put(t+"",t);
},String.valueOf(i)).start();
}
// 读
for (int i = 1; i <= 5; i++) {
final int t = i;
new Thread(()->{
myCache.get(t+"");
},String.valueOf(i)).start();
}
}
}
// 自定义缓存
class MyCache {
volatile Map<String, Object> map = new HashMap<>();
// 存、写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "开始写入");
map.put(key,value);
System.out.println(Thread.currentThread().getName() + "成功写入");
}
// 取、读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "开始读取");
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "成功读取");
}
}
// 加锁的自定义缓存
class MyCacheLock {
volatile Map<String, Object> map = new HashMap<>();
// ReadWriteLock相对于Lock是更加细粒度的控制
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// Lock lock = new ReentrantLock();
// 存、写的时候、只希望只有一个线程写
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写入");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "成功写入");
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取、读
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始读取");
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "成功读取");
} finally {
readWriteLock.readLock().unlock();
}
}
}
(五) 线程池
1. newFixedThreadPool
/**
*
* 线程池
* 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
* 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
* 好处:
* 提高响应速度(减少了创建新线程的时间)
* 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
* 便于线程管理(....)
* corePoolSize:核心池的大小maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间后会终止
* JDK 5.0起提供了线程池相关API: ExecutorService和Executors
* ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
* void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
* <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行
* Callable
* void shutdown()∶关闭连接池
* Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
*/
//测试线程池
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread3());
service.execute(new MyThread3());
service.execute(new MyThread3());
service.execute(new MyThread3());
//关闭连接
service.shutdown();
}
}
class MyThread3 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
2. newFixedThreadPool
/**
*
* 背景:
* 经常创建和销毁线程,消耗特别大的资源,比如并发的情况下的线程,对性能影响很大。
* 线程池就是问题为了解决这个问题,提前创建好多个线程,放在线程池中,使用时直接获取,使用完放回线程池中,可以避免频繁的创建、销毁,实现重复利用。
*
* 优点:
* 提高相应速度(减少创建线程的时间)
* 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 便于线程管理:corePoolSize:核心池的大小。maximumPoolSize:最大线程数。keepAliveTime:线程没有任务时最多保持多长时间后终止。
*
* 线程池相关的API:ExecutorService:线程池接口。
* 常见的实现类:ThreadPoolExecutor。
*
* void execute(Runnable command):执行任务命令,没有返回值,一般用来执行Runnable.
*
* <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般用来执行Callable
*
* void shutdown():关闭连接池
*
* Executors:工具类,线程池的工厂类,用来创建并返回不同类型的线程池
*
*/
public class MyPool {
public static void main(String[] args) {
// 1、创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
MyThread myThread = new MyThread();
// 执行
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
// 关闭连接
service.shutdown();
}
}
// 演员
class MyThread implements Runnable {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName());
}
}
}
3. newCachedThreadPool
/**
线程池
5.1 - 概念
线程池就是首先创建一些线程,他们的集合称之为线程池。线程池在系统启动时会创建大量空闲线程,程序将一个任务传递给线程池,线程池就会启动一条线程来执行这个任务,执行结束后线程不会销毁(死亡),而是再次返回到线程池中成为空闲状态,等待执行下一个任务。
5.2 - 线程池的工作机制
在线程池的编程模式下,任务是分配给整个线程池的,而不是直接提交给某个线程,线程池拿到任务后,就会在内部寻找是否有空闲的线程,如果有,则将任务交个某个空闲线程。
5.3 - 使用线程池的原因
多线程运行时,系统不断创建和销毁新的线程,成本非常高,会过度的消耗系统资源,从而可能导致系统资源崩溃,使用线程池就是最好的选择。
5.4 - 可重用线程
方法名 说明
Executors.newCacheThreadPoll(); 创建一个可缓存的线程池
execute(Runnable run) 启动线程池中的线程
*/
public class MyPool2 {
public static void main(String[] args) {
//创建线程池
ExecutorService threadPoll = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
//如果不睡眠,那么第一个执行完的线程无法及时成为空闲线程,那么线程池就会让一个新的线程执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每次循环都会开启一个线程
threadPoll.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在被执行!~");
}
});
}
threadPoll.shutdown();//关闭线程池
//线程池是无限大,当执行当前任务时,上一个任务已经完成,会重复执行上一个任务的线程,而不是每次使用新的线程
}
}
4. 3大方法
/**
* 线程池:3大方法、7大参数、4种拒绝策略
* 池化技术:线程池、连接池、内存池、对象池。
* 线程复用、可以控制最大并发数、管理线程。
*/
public class Thread9 {
public static void main(String[] args) {
// 单个线程
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 创建一个固定的线程池大小
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 可伸缩的、遇强则强、遇弱则弱
ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 100; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "ok");
});
}
} finally {
threadPool.shutdown();
}
}
}
5. 7大参数
/**
* 7大参数
*
* 特别注意:线程池不允许使用Executors创建、而是通过ThreadPoolExecutor的方式
* 因为FixedThreadPool和SingleThreadExecutor允许请求的的队列长度是Integer.MAX_VALUE、会堆积大量请求、导致OOM
* 因为CachedThreadPool和ScheduledThreadPool允许创建线程数量是Integer.MAX_VALUE、会创建大量线程、导致OOM
*
* 源码分析1
* public static ExecutorService newSingleThreadExecutor() {
* return new Executors.FinalizableDelegatedExecutorService
* (new ThreadPoolExecutor(1, 1,
* 0L, TimeUnit.MILLISECONDS,
* new LinkedBlockingQueue<Runnable>()));
* }
*
* 源码分析2
* public static ExecutorService newFixedThreadPool(int nThreads) {
* return new ThreadPoolExecutor(nThreads, nThreads,
* 0L, TimeUnit.MILLISECONDS,
* new LinkedBlockingQueue<Runnable>());
* }
*
* 源码分析3
* public static ExecutorService newCachedThreadPool() {
* return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
* 60L, TimeUnit.SECONDS,
* new SynchronousQueue<Runnable>());
* }
*/
public class Thread10 {
// 重点:本质:ThreadPoolExecutor7大参数
//public ThreadPoolExecutor(int corePoolSize, 核心线程池大小
// int maximumPoolSize, 最大核心线程池大小
// long keepAliveTime, 超时了没有人调用就会释放
// TimeUnit unit, 超时单位
// BlockingQueue<Runnable> workQueue, 阻塞队列
// ThreadFactory threadFactory, 线程工厂
// RejectedExecutionHandler handler) 拒绝策略
// 重点:本质:4种拒绝策略
// ThreadPoolExecutor.AbortPolicy 银行满了、还有人进来、不处理这个人的、抛出异常
// ThreadPoolExecutor.CallerRunsPolicy 哪来的去哪里
// ThreadPoolExecutor.DiscardPolicy 队列满了、丢掉任务、不会抛出异常
// ThreadPoolExecutor.DiscardOldestPolicy} 队列满了、尝试去和最早的竞争、不会抛出异常
}
(6)自定义线程池
public class Thread11 {
public static void main(String[] args) {
// 自定义线程池:池的大小如何设置
// 策略:CPU密集型:几核、就是几、可以保持cpu的效率最高
// 获取CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors());
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
1,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
// 最大承载:Queue + max、超过报异常
try {
for (int i = 1; i <= 9; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "ok");
});
}
} finally {
threadPool.shutdown();
}
}
}
(7)ThreadLocal
/**
*
* ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。
* ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
*
* ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:
*
* 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
* 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
*
* ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。
* 当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。
*
* 总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景
*
* ThreadLocal与synchronized有本质的区别:
* 1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
* 2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本
* ,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
* 而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
*
* 一句话理解ThreadLocal是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),
* 虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,
* 也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,
* 但是在同一个线程中这个value变量地址是一样的。
*
*/
public class ThreadLocaDemo {
private static ThreadLocal<String> localVar = new ThreadLocal<String>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localVar.get());
//清除本地内存中的本地变量
localVar.remove();
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
public void run() {
ThreadLocaDemo.localVar.set("local_A");
print("A");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
},"A").start();
Thread.sleep(1000);
new Thread(new Runnable() {
public void run() {
ThreadLocaDemo.localVar.set("local_B");
print("B");
System.out.println("after remove : " + localVar.get());
}
},"B").start();
}
}
(8)ThreadLocal
public class MyThreadLocal {
public static void main(String[] args) {
ThreadLocal<String> tl = new ThreadLocal<>();
//获取当前main线程对象绑定的值
String s = tl.get();
System.out.println(s); //null
//给当前main线程对象绑定值
tl.set("hello ThreadLocal");
String s1 = tl.get();
System.out.println(s1); //hello ThreadLocal
//开启一个新线程
SubThread subThread = new SubThread(tl);
subThread.start();
}
}
class SubThread extends Thread {
private ThreadLocal<String> tl;
public SubThread(ThreadLocal tl){
this.tl = tl;
}
@Override
public void run() {
//获取当前线程对象上绑定的值
String s = tl.get();
System.out.println("当前线程对象绑定的值:"+s); //当前线程对象绑定的值:null
tl.set("芜湖");
String s1 = tl.get();
System.out.println("当前线程对象绑定的值:"+s1); //当前线程对象绑定的值:芜湖
}
}
(六) 线程工具类
(1)CountDownLatch
/*
减少计数(CountDownLatch)
作用:就是一个或者多个线程在开始执行操作之前,必须要等到其他线程执行完成才可以执行。
参加考试的学生10个, main线程就相当于监考老师
*/
public class CountDownLatchDemo {
private static CountDownLatch countDownLatch = new CountDownLatch(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t 学生交卷:");
// 减一
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
// 等待
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t 监考老师离开教室");
}
}
(2)CyclicBarrier
/*
循环栅栏(CyclicBarrier)
作用:N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
总结:CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 await()之后方法。
CountDownLatch是一个线程等待其他线程,CyclicBarrier是线程之间相互等待。
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier =new CyclicBarrier(5, () -> System.out.println("游戏开始"));
for (int i = 1; i <=5 ; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("第"+ finalI +"进入游戏");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
},String.valueOf(i)).start();
}
}
}
(3)Semaphore
/*
信号灯(Semaphore)
信号量,用来控制同一时间,资源可被访问的线程数量,一般应用场景流量的控制。
*/
public class SemaphoreDemo {
public static void main(String[] args) {
// Semaphore
// 线程数量、停车位!限流!
// acquire()获得、假设如果已经满了、等待、等待被释放为止!
// release()释放、会将当前的信号量释放+1、然后唤醒等待的线程!
// 作用:多个共享资源互斥的使用!并发限流、控制最大线程数!
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
(七) 线程3特性
(1)原子性
// (1)原子性
//原子性的操作是不可被中断的一个或一系列操作。
public class ThreadDemo1 implements Runnable{
public static AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public void run() {
// 原子性分析:操作分解,可以分为三步、【取值,加一,赋值】,这三个操作都是原子的,不过合在一起就不行了
// 分析过程:
// no初始值是1,线程A读取no值是1,然后做no+1,这时线程B对 no取值还是1,然后A将2赋值给no,B操作no+1结果是2,也将2赋值给no。对1做了两次+1操作,最后结果是2
// A线程 no++:no(0) no(1)
// 内存 no=2
// B线程 no++:no(1) no(2)
// 解决方案:原子类(AtomicInteger等)是通过使用unsafe的compareAndSwap方法实现CAS操作保证原子性的。
atomicInteger.getAndIncrement();
System.out.println(atomicInteger.get());
}
public static void main(String[] args){
ThreadDemo1 demo = new ThreadDemo1();
// 结果有可能不是1000、多运行几次
for(int i=0;i<1000;i++){
Thread task = new Thread(demo);task.start();
}
}
}
(2)可见性
/**
*
* JMM(java内存模型)
*
* CPU和内存之间隔着缓存和CPU寄存器。缓存还分为一级、二级、三级缓存。
* CPU的读写性能上要大于内存,为了提高效率会将数据先取到缓存中,CPU处理完数据后会先放到缓存中,然后同步到内存中。
*
* (2)可见性
*
*/
public class ThreadDemo2 {
// volatile 不加volatile没有可见性
private static volatile boolean result = true;
public static void main(String[] args) {
//工作内存
new Thread(()->{
while (result){
}
}).start();
//主内存
try {
//让main线程睡眠俩秒
TimeUnit.SECONDS.sleep(2);
//将result修改为false
result = false;
//输出标记符号
System.out.println("当前result的值为:" + result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(3)有序性
package demo6;
// (3)有序性(volatile)
public class ThreadDemo3 {
private volatile static int x = 0, y = 0;
private volatile static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
int i=0;
while (true) {
i++;
x = 0;
y = 0;
a = 0;
b = 0;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
//用于调整两个线程的执行顺序
shortWait(20000);
a = 1;
x = b;
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
b = 1;
y = a;
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("第" + i + "次(" + x + "," + y + ")");
if (x==0&&y==0){
break;
}
}
}
public static void shortWait(long interval){
long start = System.nanoTime();
long end;
do{
end = System.nanoTime();
}while(start + interval >= end);
}
}