CAS
结合 高并发 文章看!!!! 要不然看不懂
compare and swap. :
比较 和 交换
在多线程 没有锁的 情况下 可以保证 多线程对一个 共享变量的 更新
使用:
拿到内存中的最新值,使用CAS尝试将内存的值修改成目标值 如果修改失败,
则获取内存位置的最新值,然后重新尝试,直到修改成功
注意:
当前值,内存中最新值,目标值
当前值 与 内存中最新值 进行比较 相等 就更新 目标值 不相等 说明 其他线程修改了这个共享变量的值 则然后重新尝试读取 当前值 与内存中最新值进行比较,直到修改成功
CAS 缺点:
开销大 一直do while
它是可以保证原子性的
底层代码
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
开销大 那么怎么办呢?
ABA的问题 :
表示 原始共享变量是 0
1号线程 读取的时候是 0 这个时候
2号线程 读取0 修改成 2
3号线程 读取 2 修改成 0
这个时候 1号线程 读取的 0 实际上 是 变了样的 0
这就是 aba 问题 。
就好比:
你女朋友 分手之后 找了一个男的 之后 又跟你 复合了
所以 中间发生了什么 你要感知到 才行 。
解决ABA问题:
共享变量 再 添加一个 版本号 即可
就是 读取共享变量的时候 不仅读取值 也要 读取版本号
那么 CAS 的底层实现是什么呢???
cas 和 sychroned 和 volatile 的底层实现 都一样
JUC
我们使用 多线程的目的 :
就是提高效率
就是 尽可能的 利用 cpu资源 尽可能的利用系统资源
如果 多线程使用 不合理 那么 可能导致性能 还不如 单线程 导致性能 更低
因为 多线程消耗的资源更多 涉及到 线程之间的调度 cpu 上下文之间的 切换
以及 线程的 创建及销毁 还有同步 都是多线程的问题。
jdk 1.5 之前 :
sychornizde 和 volitail
就提供这两个 用于多线程的
1.5之后 jdk 提供了 java.util.current 包 这里面提供了 大量 线程相关的 工具类
那么 多线程 就是 你
如何利用更多的资源 做更多的事情
volatile 关键字
1.可见性
2. 有序性
它是不保证 原子性的
package com.juc;
public class VolatileAPP {
public static void main(String[] args) {
/**
*
*/
Data data = new Data();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
data.flag = true;
System.out.println("flag = " +data.isFlag());
}).start();
while(true){
if(data.isFlag()){
System.out.println("-------------");
break;
}
}
}
}
class Data {
boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
你可以使用 Data implements Runnable 来实现 线程里面的 run方法
我个人是不习惯 还是喜欢使用 lamda表达式的方式 因为我 scala 用的比 java熟悉
上面的代码:
两个线程:
Thread 用来 修改 flag值
main 用来 反复while ture 去读取flag值
那么 上面的 -----会被打印出来吗??
以及 while true 能不能结束?
结果:
说明 两个线程之间 共享数据 是不可见的
因为 thread 线程 以及 把 flag 修改成 true
而 main 线程 读取的 flag 却是 flag
说明是有问题的:
说明 两个线程之间 共享数据操作 是不可见的
解决:
1. synchronized
这个关键字 你加在哪???看高并发的文章!!
1.加在 isFlag方法那块
2.或者 你加在 while true 里面的if 都可以
2. volatile
class Data {
boolean flag = false;
public synchronized boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
我修改的是 这
synchronized 效率极低 :
加锁这种效率是最低的
如果多个线程 来 访问isFlag 方法 那么 需要判断 会发生阻塞! 效率很低
使用 volatile :
他就是 多线程情况下操作 共享数据 是可以保证 共享数据的可见性
即:
class Data {
volatile boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
但是 volatile 是不能保证 原子性的!
原子性:
你可以理解为 :
一次操作 要么成功 要么失败 所以你一晚上 几次??
原子性
i++ 的原子性问题!:
很好的一个案例
结合cpu cache : 你可以知道
i++ 操作 实际上是 三步:
1.从主存 读取 i 放到 cpu cache里
2. 线程 读取 cpu cache值 进行修改
3. 修改后的 cpu cache值 写回 主存
读 --》修改---》写回
这三步 每一步都是具有原子性的 但是
三步结合在一起 是不具有原子性的!!!
package com.juc;
public class AtomicApp {
public static void main(String[] args) {
AtomicData atomicData = new AtomicData();
for (int i = 0; i <20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
atomicData.increate();
}
},"AtomicApp"+i).start();
}
while (Thread.activeCount() > 2) { //>2 因为一个 main 一个gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);
//单线程
// for (int j = 0; j <1000 ; j++) {
// atomicData.increate();
// }
//
// System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);
}
}
class AtomicData {
int number = 0;
public int increate(){
return number++;
}
}
结果:
单线程下 结果是 1000 是没有问题的
多线程情况下 正常结果应该是 20000 但是 不是!!
这就说明 number++; 操作 是不具有原子性 在多线程下 是用问题的!!!
但是真正的理解是:
number ++ 是三步操作
多线程下 你会觉得是 可见性导致的 但是 不准确 因为 原子性!!
如果是可见性 你加一个volatile 即可 但是 number++ 三步在一起 是不具有原子性的
volatile 是不能解决 原子性的
要想解决:
1。原子变量
jdk1.5之后提供的 java.util.concurrent.atomic包下
2.sychronized
package com.juc;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicApp {
public static void main(String[] args) {
AtomicData atomicData = new AtomicData();
for (int i = 0; i <20 ; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
atomicData.increase2();
}
},"AtomicApp"+i).start();
}
while (Thread.activeCount() > 2) { //>2 因为一个 main 一个gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);
System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.atomicInteger);
//单线程
// for (int j = 0; j <1000 ; j++) {
// atomicData.increate();
// }
//
// System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);
}
}
class AtomicData {
int number = 0;
//第一种解决
public synchronized int increate(){
return number++;
}
//第二种解决
AtomicInteger atomicInteger = new AtomicInteger();
public void increase2(){
atomicInteger.getAndIncrement();
}
}
文章开始提到:
CAS算法是可以解决 原子性问题
那么原子类 就是调用 CAS算法!! 文章最开始就是源码
原子变量:
以AtomicInteger 为例 看源码
AtomicInteger源码:
public class AtomicInteger extends Number implements java.io.Serializable {
private volatile int value;
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
getAndIncrement 实现:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
compareAndSwapInt 实现的就是 cas算法
结论:原子变量
1. volatile int value; 保证值 的 可见性
2. CAS算法保证 数据的原子性
CAS:
当前值,内存中最新值,目标值
可以理解为:
内存值V
内存中最新值A
更新值B
当且仅当 V==A ,把 B的值 赋值给 V 即 多线程情况下 只有一个线程会成功
cas效率高 是因为 它不会阻塞 但是如果要自己实现cas的 需要对 为修改成功后的步骤该
如何做 要自己实现 类似原子类里面的这个方法!!
下图 就是cas 过程
模拟CAS算法
package com.juc;
public class CASAPPComplement {
public static void main(String[] args) {
CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i <10 ; i++) {
new Thread(()->{
int expectValue = cas.get();
boolean sucess = cas.compareAndSet(expectValue, (int) (Math.random() * 101));
System.out.println("是否成功:"+sucess);
}).start();
}
}
}
class CompareAndSwap{
int value;
//获取内存值
public synchronized int get(){
return value;
}
//比较
public synchronized int compareAndSwap(int expectValue, int newValue) {
int oldValue = value;
if (oldValue == expectValue) {
this.value = newValue;
}
return oldValue;
}
//设置值
public synchronized boolean compareAndSet(int expectValue, int newValue){
return expectValue == compareAndSwap(expectValue, newValue);
}
}
底层 绝对不是用 synchronized 这个的哈
ConcurrentHashMap 锁分段机制
是一个线程安全的 HashMap
它是jdk1.5 之后那个包 里面提供的
那么HashMap 和HashTable 有什么区别??
HashMap 是线程不安全的
HashTable 是线程安全的 效率低!锁表 实际上就是 并行转换成 串行的一个操作!!
如果HashTable 遇到 复合操作 就是线程不安全的!:
eg:
若不存在就添加
if(!table.contants()){
table.put
}
contants 和put 方法各自具有 独立的锁 但是 和在一起 就不具有独立的锁 有线程问题!!
ConcurrentHashMap:锁分段机制
concurrentLevel : 分段级别 默认的级别是16
也就是ConcurrentHashMap 里面有16 段 每一段 叫 segment
每个 segment 有独立的表
好处就是 :
每个 segment 都有一个独立的锁
那么 多线程情况下 就可以并发访问不同的 segment 效率高
1.8 之后 每隔segment 采用的是 cas 1.5采用的是 锁
注意:
ConcurrentHashMap -》HashMap
ConcurrentSkipListMap -》TreeMap
CopyOnWriteArrayList -》 ArrayList
CopyOnWriteArraySet -> ArrayAet
这些多线程容器都优于 jdk原生的
那么他们都解决什么问题呢??
package com.juc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* 写入 并复制
*/
public class CopyOnWriteArrayListApp {
public static void main(String[] args) {
ListTest listTest = new ListTest();
for (int i = 0; i <10 ; i++) {
new Thread(()->{
Iterator<String> it = listTest.list.iterator();
//边取 边加
while (it.hasNext()) {
System.out.println(it.next());
listTest.list.add("AA");
}
}).start();
}
}
}
class ListTest{
static List<String> list = Collections.synchronizedList(new ArrayList<String>());
static {
list.add("AA");
list.add("BB");
list.add("CC");
}
}
结果:
发生 并发修改 异常
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.juc.CopyOnWriteArrayListApp.lambda$main$0(CopyOnWriteArrayListApp.java:24)
at java.lang.Thread.run(Thread.java:748)
因为 读取 和添加 操作的 是 同一个数据源 就会发生 并发修改异常! 你一个线程跑 也是这个问题
也就是:
不能 读取 和添加 操作的 是 同一个数据源
使用 CopyOnWriteArrayList 可以解决
class ListTest{
// static List<String> list = Collections.synchronizedList(new ArrayList<String>());
static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
static {
list.add("AA");
list.add("BB");
list.add("CC");
}
}
注意:
但是 添加操作 不适合它 因为是写入并复制 复制!!
并发的迭代 操作 它的效率很高!!
CountDownLatch 闭锁
也就是
当你要完成一些运算 这些运算没有完成 那就等待这些运算完成
我在继续执行!!
package com.juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchApp {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(10);
long start = System.currentTimeMillis();
//打印 5w 以内偶数
for (int i = 0; i <10 ; i++) {
new Thread(()->{
for (int j = 0; j <50000 ; j++) {
if (j % 2 == 0) {
System.out.println(j);
}
}
}).start();
}
long end = System.currentTimeMillis();
System.out.println("执行时间: " +(end -start));
}
}
上面代码 想 计算:
10个线程 做 5w以内的偶数打印 的执行时间
是没有办法计算的 :
因为:
main 和 10个线程 是同时执行的 上面的代码 算得不准确
不能保证 10个线程 执行完了 再计算 执行时间 !!!!
所以有个执行的先后顺序的 !!!
解决这个问题:如下
必须用到 闭锁!!
package com.juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchApp {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
long start = System.currentTimeMillis();
//打印 5w 以内偶数
for (int i = 0; i <10 ; i++) {
new Thread(()->{
for (int j = 0; j <50000 ; j++) {
if (j % 2 == 0) {
System.out.println(j);
}
}
latch.countDown();
}).start();
}
latch.await();
long end = System.currentTimeMillis();
System.out.println("执行时间: " +(end -start) +"ms");
}
}
结果:
CountDownLatch latch = new CountDownLatch(10);
10 : 完成一个线程操作 就递减1 直到减到 0
main 线程 才执行
所以:
1. 10个线程里 要有 latch.countDown();
2. main 有一个 latch.await()
所以 闭锁这个操作 应用很广泛 很重要!!
Lock同步锁
用于 解决多线程安全的方式:
1. 同步代码块 synchronized 可以称为隐式锁
2. 同步方法 synchronized 可以称为隐式锁
jdk1.5 之后 更灵活的方式!!!
3. 同步锁
是一个显示锁 需要 通过lock() 方法上锁,
必须通过unlock() 方法进行释放锁
注意:
必须通过unlock() 方法进行释放锁
如果不释放会有很多问题 最好放在 finally里面
而 synchronized 就不需要 它是 jvm 底层帮自己维护
package com.juc;
import java.util.concurrent.atomic.AtomicInteger;
public class LockApp {
public static void main(String[] args) {
Ticket ticket = new Ticket();
for (int i = 0; i < 3; i++) {
new Thread(()->{
while (true) {
if (ticket.getTicket() > 0) {
System.out.println(Thread.currentThread().getName()+" 完成售票,余票为:"+ --ticket.ticket);
// System.out.println(Thread.currentThread().getName()+" 完成售票,余票为:"+ ticket.a.decrementAndGet());
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},i+"号窗口").start();
}
}
}
class Ticket{
int ticket = 100;
// AtomicInteger a = new AtomicInteger(100);
public int getTicket() {
return ticket;
}
}
上面的一个经典案例:
卖票!!
你可以使用 原子类来解决
那么使用 Lock 该如何做呢??
package com.juc;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockApp {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Lock lock = new ReentrantLock();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
//上锁
lock.lock();
try {
while (true) {
if (ticket.getTicket() > 0) {
System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --ticket.ticket);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
//释放锁
lock.unlock();
}
}, i + "号窗口").start();
}
}
}
class Ticket {
int ticket = 100;
public int getTicket() {
return ticket;
}
}
等待唤醒机制
synchronized:
它是 wait 和 notify 来完成 但是效率低 对吧!!
那么 如何使用 lock 同步锁 来实现 等待唤醒机制!!!呢??
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer").start();
new Thread(()->{
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer").start();
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
if (product >= 10) {
System.out.println("产品已满!");
}else {
System.out.println(Thread.currentThread().getName()+" : " + ++product);
}
}
//卖货
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
}else {
System.out.println(Thread.currentThread().getName()+" : " + --product);
}
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
结果:
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
缺货!
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
producer : 10
产品已满!
产品已满!
上面是 生产者和消费者案例 :
1.没有采用 等待唤醒机制 会有什么问题
生产者 不断发送数据 消费值不断消费 !!
会造成 数据丢失问题!! 或者数据重复消费 问题!!
就看 谁生产快 和消费快了 这是有问题的!!
加入等待唤醒机制!
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer").start();
new Thread(()->{
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer").start();
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
if (product >= 10) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
}
//卖货
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
结果:
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
producer : 10
产品已满!
consumer : 9
consumer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0
缺货!
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
consumer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0
只要发现:
生产满了 就赶紧消费
缺货 就赶紧生产
这样才是 有效的数据!!
这种是效率低的问题吗???
改一下 :
把生产者 生产货物 变成1
消费完 再 睡200ms
会造成 程序一只运行 不会结束!!
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer").start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer").start();
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
if (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
}
//卖货
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
问题发生在:
sale 和 get方法 里面的 else那块
因为:
当product =0
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
代码运行在 this.wait(); 这
this.wait():
等在这个位置 同时释放锁的资源
当他被唤醒之后 从this.wait() 代码的位置 往下继续执行
那么此时 生产者线程:
public synchronized void get(){
if (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
}
代码运行 else ++product 导致product 变为1
然后 this.notifyAll(); 释放锁 之后
此时 开始生产者和消费值继续抢占cpu 如果 消费者 抢到!!
消费者会 接着从 sale 方法里面的 this.wait(); 往下执行 跳出else 就是消费者
执行完毕ok了
就剩下 生产者一个线程了
接着 生产者 发现 product=1 会运行
System.out.println("产品已满!");
this.wait(); 释放锁等待被 唤醒 !!! 但是 没有消费者线程 了
所以不会被唤醒!!
所以代码会卡住在 : 程序结束不了!!!
产品已满 或者 缺货
解决:
把else 去掉
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer").start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer").start();
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
if (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
程序可以结束!!
还是有问题的!!:
多个线程 就会有问题
上面是一个生产者一个消费值线程 是没有问题的
那么 两个消费者 两个生产者 就会有问题!!
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
for (int j = 0; j < 2; j++) {
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer"+j).start();
new Thread(()->{
// try {
//// Thread.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer"+j).start();
}
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
if (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
结果:
producer0 : 1
产品已满!
consumer0 : 0
缺货!
producer1 : 1
产品已满!
producer0 : 2
产品已满!
producer1 : 3
产品已满!
consumer0 : 2
consumer0 : 1
consumer0 : 0
缺货!
缺货!
producer1 : 1
产品已满!
producer0 : 2
产品已满!
producer1 : 3
产品已满!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺货!
consumer0 : -1
缺货!
consumer1 : -2
缺货!
producer1 : -1
producer1 : 0
producer1 : 1
产品已满!
producer0 : 2
产品已满!
producer1 : 3
产品已满!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺货!
consumer0 : -1
缺货!
consumer1 : -2
缺货!
producer1 : -1
producer1 : 0
producer1 : 1
产品已满!
producer0 : 2
产品已满!
producer1 : 3
产品已满!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺货!
consumer0 : -1
缺货!
consumer1 : -2
缺货!
producer1 : -1
producer1 : 0
producer1 : 1
产品已满!
producer0 : 2
产品已满!
都出现负数了!!
如何导致的:
public synchronized void sale(){
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
两个线程 都进到这个方法 :
都停在 this.wait(); 如果 同时被 唤醒
product=0 --product之后 就出现负数了
这就是虚假唤醒问题!!!
官网的解释是 :
this.wait(); 必须使用在 while
所以要把 if 变成 while 为什么呢??
就是 要再判断一次!!! 就解决了!虚假唤醒问题!
package com.juc;
public class ProducerAndConsumerApp {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
for (int j = 0; j < 2; j++) {
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer"+j).start();
new Thread(()->{
// try {
//// Thread.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer"+j).start();
}
}
}
//店员
class Clerk{
int product = 0;
//进货
public synchronized void get(){
while (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
while (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + --product);
this.notifyAll();
}
}
//producer
class Producer{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Consumer{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
}
那么 以上是 使用 synchronized 和 wait + notify的方式 完成等待唤醒机制
如何使用lock完成等待唤醒机制呢???
lock 完成等待唤醒机制
wait 和 notify
Lock 有自己的
叫 Condition 控制线程通信
await、signal、signalAll
Condition 是通过特定的Lock获取 newCondition
就可以使用等待唤醒机制了!!!
package com.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerAndConsumerLockApp {
public static void main(String[] args) {
Clerk01 clerk = new Clerk01();
Producer01 producer = new Producer01(clerk);
Consumer01 consumer = new Consumer01(clerk);
for (int j = 0; j < 2; j++) {
new Thread(()->{
//生产货物
for (int i = 0; i <20 ; i++) {
producer.clerk.get();
}
},"producer"+j).start();
new Thread(()->{
//卖货
for (int i = 0; i <20 ; i++) {
consumer.clerk.sale();
}
},"consumer"+j).start();
}
}
}
//店员
class Clerk01 {
int product = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//进货
public void get(){
lock.lock();
try{
while (product >= 1) {
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + ++product);
condition.signalAll();
}finally {
lock.unlock();
}
}
//卖货
public void sale(){
lock.lock();
try{
while (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : " + --product);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
//producer
class Producer01 {
Clerk01 clerk;
public Producer01(Clerk01 clerk) {
this.clerk = clerk;
}
}
class Consumer01 {
Clerk01 clerk;
public Consumer01(Clerk01 clerk) {
this.clerk = clerk;
}
}
线程按序交替
eg:
编写一个程序,开启3个线程,这三个线程的ID分别是A、B、C、每个线程
将自己的ID在屏幕上打印10遍,要求输出的结果必须按顺序显示。
如:
ABCABCABC。。。ABC依次递归
分析:
1.对线程控制 需要 Lock
2.对线程 按顺序控制 就需要 Condition
package com.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ABCAPP {
public static void main(String[] args) {
ABC abc = new ABC();
new Thread(()->{
for (int i = 0; i < 10; i++) {
abc.printA(i);
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
abc.printB(i);
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
abc.printC(i);
System.out.println("---------------------------");
}
},"C").start();
}
}
class ABC{
//当前正在执行线程的标记
int number =1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
/**
*
* @param totalprint 表示循环几轮
* @throws InterruptedException
*/
public void printA(int totalprint) {
lock.lock();
try{
//1.判断
if(number !=1){
condition1.await();
}
//2.打印
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
}
//3.唤醒
number =2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(int totalprint) {
lock.lock();
try{
//1.判断
if(number !=2){
condition2.await();
}
//2.打印
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
}
//3.唤醒
number =3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(int totalprint) {
lock.lock();
try{
//1.判断
if(number !=3){
condition3.await();
}
//2.打印
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
}
//3.唤醒
number =1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
上面的代码 :
是 10轮 打印10次
结果:
A 0 0
A 1 0
A 2 0
A 3 0
A 4 0
A 5 0
A 6 0
A 7 0
A 8 0
A 9 0
B 0 0
B 1 0
B 2 0
B 3 0
B 4 0
B 5 0
B 6 0
B 7 0
B 8 0
B 9 0
C 0 0
C 1 0
C 2 0
C 3 0
C 4 0
C 5 0
C 6 0
C 7 0
C 8 0
C 9 0
---------------------------
A 0 1
A 1 1
A 2 1
A 3 1
A 4 1
A 5 1
A 6 1
A 7 1
A 8 1
A 9 1
B 0 1
B 1 1
B 2 1
B 3 1
B 4 1
B 5 1
B 6 1
B 7 1
B 8 1
B 9 1
C 0 1
C 1 1
C 2 1
C 3 1
C 4 1
C 5 1
C 6 1
C 7 1
C 8 1
C 9 1
---------------------------
A 0 2
A 1 2
A 2 2
A 3 2
A 4 2
A 5 2
A 6 2
A 7 2
A 8 2
A 9 2
B 0 2
B 1 2
B 2 2
B 3 2
B 4 2
B 5 2
B 6 2
B 7 2
B 8 2
B 9 2
C 0 2
C 1 2
C 2 2
C 3 2
C 4 2
C 5 2
C 6 2
C 7 2
C 8 2
C 9 2
---------------------------
ReadWriteLock读写锁
就是读写分离:
什么情况下使用呢?
1.写 多线程 就是 写写 要互斥
2.读写 读和写 也要互斥
3.读读 是不需要互斥的
package com.juc;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteApp {
public static void main(String[] args) {
/**
* 那么一个线程去 写 100 个线程去读
*/
Demo demo = new Demo();
new Thread(()->{
demo.set((int)(Math.random()*101));
},"write").start();
for (int i = 0; i < 100; i++) {
new Thread(()->{
demo.get();
},"read").start();
}
}
}
class Demo{
int number = 0;
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//读
public void get(){
readWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+" : " +number);
}finally {
readWriteLock.readLock().unlock();
}
}
//写
public void set(int number){
readWriteLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName());
this.number = number;
}finally {
readWriteLock.writeLock().unlock();
}
}
}
结果:
write
read : 36
read : 36
read : 36
read : 36
read : 36
read : 36
。。。
一个线程再 写 100个线程再 读 这就是 读写锁
线程八锁
1.1.两个普通同步方法 两个线程 标准打印 , //one two
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number.getTwo();
}).start();
}
}
class Number{
public synchronized void getOne(){
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
}
结果:
one
two
2. 新增 Thread.sleep 给 getone方法
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number.getTwo();
}).start();
}
}
class Number{
public synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
}
结果:
one
two
3.新增 getThree 普通方法
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number.getTwo();
}).start();
new Thread(()->{
number.getThree();
}).start();
}
}
class Number{
public synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
public void getThree(){
System.out.println("three");
}
}
结果:
three
one
two
4.两个普通同步方法 两个Number对象 打印?
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number2 = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number2.getTwo();
}).start();
// new Thread(()->{
//
// number.getThree();
// }).start();
}
}
class Number{
public synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
// public void getThree(){
// System.out.println("three");
// }
}
结果:
two
one
5.修改 getOne为 静态的同步方法 打印? two one
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
// Number number2 = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number.getTwo();
}).start();
// new Thread(()->{
//
// number.getThree();
// }).start();
}
}
class Number{
public static synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
// public void getThree(){
// System.out.println("three");
// }
}
结果:
two
one
6. 修改两个同步方法都为静态方法
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
// Number number2 = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number.getTwo();
}).start();
// new Thread(()->{
//
// number.getThree();
// }).start();
}
}
class Number{
public static synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public static synchronized void getTwo(){
System.out.println("two");
}
// public void getThree(){
// System.out.println("three");
// }
}
结果:
one
two
7. 一个静态同步方法 一个非静态同步方法 两个Number对象
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number2 = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number2.getTwo();
}).start();
// new Thread(()->{
//
// number.getThree();
// }).start();
}
}
class Number{
public static synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
// public void getThree(){
// System.out.println("three");
// }
}
结果:
two
one
8.两个静态同步方法 两个Number对象
package com.juc;
/**
* 判断 打印的是 one 还是 two
*
* 1.两个普通同步方法 两个线程 标准打印 , //one two
*
*/
public class Thread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number2 = new Number();
new Thread(()->{
number.getOne();
}).start();
new Thread(()->{
number2.getTwo();
}).start();
// new Thread(()->{
//
// number.getThree();
// }).start();
}
}
class Number{
public static synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public static synchronized void getTwo(){
System.out.println("two");
}
// public void getThree(){
// System.out.println("three");
// }
}
结果:
one
two
总结:
线程8锁 关键:
1.非静态方法的锁 默认为 this 静态方法的锁 对应的 Class 实例 xxx.class
2.某一个时刻内 只能有一个线程持有锁 无论几个方法
线程池
为什么使用线程池呢??
之前使用线程 是 new Thread 的方式
这样是 需要就创建一个线程
实际上就是 创建线程 然后销毁线程
如果频繁的创建和销毁的话,
那么也是非常耗费资源的。
那么 你一定使用过数据库的连接池 。
为什么你使用它呢?
性能好呀
为什么好呢?
1.不是频繁的创建和销毁
2.是去 线程池里面 取连接就好 用完放回去 (池子里 先准备好 一些连接)
线程池:1.5之后 线程的使用和调度的 接口 是 EXecutor
1.提供了一个线程队列 存的是 所有等待状态的线程
避免创建和销毁的开销 提高效率
2.体系结构:
1.Executor : 使用+调度
2.ExecutorService :线程池的主要接口
3.ThreadPoolExecutor : 线程池的实现类
4.ScheduledExecutorSerivce : 线程的调度
5.ScheduledThreadPollExecutor:实现了4 继承了3
具备线程池和线程的调度的功能
3.工具类:Executors 提供了很多方法!!
eg:
newFixedThreadPoll() 创建固定大小的线程池
newCachedThreadPoll() 线程池的数量不固定 可以根据需求自动更改数量
newSingleThreadExecutor() 创建单个线程池 线程池里只有一个线程
newScheduledThreadPoll() 创建固定大小的线程 可以延迟定时 的执行任务
package com.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPollApp {
public static void main(String[] args) {
Poll poll = new Poll();
//1.创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//2.为线程池中的线程 分配任务
executorService.submit(new Thread(()->{
while (poll.i < 100) {
System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
}
}));
//3.关闭线程池
/**
* shutdown :
* 等待 现有的线程池中的 线程任务完成之后 再关闭 来新的资源 也不接受了
*
* shutdownNow:
* 立即关闭
*/
executorService.shutdown();
}
}
class Poll{
int i =0;
}
结果:
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 : 5
pool-1-thread-1 : 6
pool-1-thread-1 : 7
pool-1-thread-1 : 8
pool-1-thread-1 : 9
pool-1-thread-1 : 10
pool-1-thread-1 : 11
pool-1-thread-1 : 12
pool-1-thread-1 : 13
pool-1-thread-1 : 14
pool-1-thread-1 : 15
pool-1-thread-1 : 16
pool-1-thread-1 : 17
pool-1-thread-1 : 18
pool-1-thread-1 : 19
。。。
executorService.submit(new Thread(()->{
while (poll.i < 100) {
System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
}
}));
只有一个一个线程 分配任务
package com.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPollApp {
public static void main(String[] args) {
Poll poll = new Poll();
//1.创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//2.为线程池中的线程 分配任务
for (int i = 0; i < 10; i++) {
executorService.submit(new Thread(()->{
while (poll.i < 100) {
System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
}
}));
}
//3.关闭线程池
/**
* shutdown :
* 等待 现有的线程池中的 线程任务完成之后 再关闭 来新的资源 也不接受了
*
* shutdownNow:
* 立即关闭
*/
executorService.shutdown();
}
}
class Poll{
int i =0;
}
结果:
pool-1-thread-2 : 1
pool-1-thread-4 : 3
pool-1-thread-4 : 5
pool-1-thread-4 : 6
pool-1-thread-4 : 8
pool-1-thread-4 : 9
pool-1-thread-3 : 2
pool-1-thread-3 : 11
pool-1-thread-3 : 12
pool-1-thread-3 : 13
pool-1-thread-3 : 14
pool-1-thread-3 : 15
pool-1-thread-3 : 16
pool-1-thread-3 : 17
pool-1-thread-3 : 18
pool-1-thread-3 : 19
pool-1-thread-3 : 20
pool-1-thread-3 : 21
pool-1-thread-3 : 22
pool-1-thread-3 : 23
pool-1-thread-3 : 24
pool-1-thread-3 : 25
pool-1-thread-3 : 26
pool-1-thread-3 : 27
pool-1-thread-3 : 28
pool-1-thread-3 : 29
pool-1-thread-1 : 0
pool-1-thread-1 : 31
pool-1-thread-1 : 32
pool-1-thread-1 : 33
pool-1-thread-1 : 34
pool-1-thread-1 : 35
pool-1-thread-1 : 36
pool-1-thread-1 : 37
pool-1-thread-1 : 38
pool-1-thread-1 : 39
pool-1-thread-1 : 40
pool-1-thread-3 : 30
pool-1-thread-3 : 42
pool-1-thread-3 : 43
pool-1-thread-3 : 44
pool-1-thread-3 : 45
pool-1-thread-3 : 46
pool-1-thread-4 : 10
pool-1-thread-4 : 48
pool-1-thread-4 : 49
pool-1-thread-4 : 50
pool-1-thread-5 : 7
pool-1-thread-5 : 52
pool-1-thread-2 : 4
pool-1-thread-2 : 54
pool-1-thread-2 : 55
pool-1-thread-2 : 56
pool-1-thread-2 : 57
pool-1-thread-2 : 58
pool-1-thread-2 : 59
pool-1-thread-2 : 60
pool-1-thread-2 : 61
pool-1-thread-2 : 62
pool-1-thread-2 : 63
pool-1-thread-2 : 64
pool-1-thread-2 : 65
pool-1-thread-2 : 66
pool-1-thread-2 : 67
pool-1-thread-5 : 53
pool-1-thread-5 : 69
pool-1-thread-4 : 51
pool-1-thread-4 : 71
pool-1-thread-4 : 72
pool-1-thread-3 : 47
pool-1-thread-3 : 74
pool-1-thread-3 : 75
pool-1-thread-3 : 76
pool-1-thread-3 : 77
pool-1-thread-3 : 78
pool-1-thread-3 : 79
pool-1-thread-1 : 41
pool-1-thread-1 : 81
pool-1-thread-1 : 82
pool-1-thread-1 : 83
pool-1-thread-1 : 84
pool-1-thread-1 : 85
pool-1-thread-1 : 86
pool-1-thread-1 : 87
pool-1-thread-1 : 88
pool-1-thread-1 : 89
pool-1-thread-1 : 90
pool-1-thread-1 : 91
pool-1-thread-3 : 80
pool-1-thread-4 : 73
pool-1-thread-5 : 70
pool-1-thread-2 : 68
pool-1-thread-5 : 95
pool-1-thread-4 : 94
pool-1-thread-3 : 93
pool-1-thread-1 : 92
pool-1-thread-3 : 99
pool-1-thread-4 : 98
pool-1-thread-5 : 97
pool-1-thread-2 : 96
pool-1-thread-5:
池1: pool-1
线程5: thread-5
明白了吗?
线程池里面 5个线程
我分配了 10个任务
callable:
package com.juc;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPollApp {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Poll poll = new Poll();
//1.创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<Integer>> list = new ArrayList<>();
//2.为线程池中的线程 分配任务
for (int j = 0; j < 10; j++) {
Future<Integer> future = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
});
list.add(future);
}
//遍历10个任务的结果
for (Future<Integer> future: list){
System.out.println(future.get());
}
//2.为线程池中的线程 分配任务
// for (int i = 0; i < 10; i++) {
//
// executorService.submit(new Thread(()->{
//
// while (poll.i < 100) {
// System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
// }
// }));
// }
//3.关闭线程池
/**
* shutdown :
* 等待 现有的线程池中的 线程任务完成之后 再关闭 来新的资源 也不接受了
*
* shutdownNow:
* 立即关闭
*/
executorService.shutdown();
}
}
class Poll{
int i =0;
}
结果:
4950
4950
4950
4950
4950
4950
4950
4950
4950
4950
线程池+调度
package com.juc;
import java.util.concurrent.*;
public class ScheduledThreadPoolApp {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
Future<Integer> future = pool.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = (int) (Math.random() * 101);
System.out.println(Thread.currentThread().getName() + " : " + num);
return num;
}
}, 1, TimeUnit.SECONDS);
System.out.println(future.get());
}
pool.shutdown();
}
}
结果:
pool-1-thread-1 : 12
12
pool-1-thread-1 : 69
69
pool-1-thread-2 : 39
39
pool-1-thread-1 : 28
28
pool-1-thread-3 : 79
79
pool-1-thread-2 : 17
17
pool-1-thread-4 : 78
78
pool-1-thread-1 : 5
5
pool-1-thread-5 : 62
62
pool-1-thread-3 : 32
32
ForkJoinPool 分支/合并
思想:
1.任务 递归分配成若干 小任务(拆到不可再拆)
2.小任务 并行求值
3.结果合并
工作窃取:
之前线程池:
1.4个内核
1号和4号 假如发生线程阻塞
2号和3号 执行完任务
那么 23是空闲状态
cpu没有更加合理的利用
jdk1.7之后 forkjoin:
工作窃取模式 :
把大任务拆分成小任务 ,把小任务 分配到不同的线程中
形成一个线程队列(里面是小任务)是双端队列 就意味着
一旦某一个线程 获取任务的时候 获取不到时候 会发生阻塞 避免阻塞发生
这个线程 会去别的线程 队列的末尾 偷一个任务进行执行!!
更好的利用cpu资源 效率高 !!
cpu几个 就有用几个线程