多线程学习
多线程学习
四、线程池&volatile
1. 线程状态
2. 线程池
线程池的原理类似于用碗吃饭,吃完后放回橱柜(就是线程池),如果这个碗正在使用(A线程),这时需要新的线程B执行,则从橱柜里重新拿碗,如果A线程使用的碗还未归还则需要拿新的碗进行吃饭;
3. 线程池-Executors
3.1 Executors.newCachedThreadPool();
//创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
//static ExecutorService newCachedThreadPool()
//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个默认线程池对象 默认是空的 默认最大是int的最大值
ExecutorService executorService = Executors.newCachedThreadPool();
//Executors 帮助我们创建线程池对象
//ExecutorService 帮追我们管理线程池
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
// Thread.sleep(2000);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.shutdown();
}
}
此时之所以是两个线程执行因为线程1执行完还未归还线程池内时,线程2已经执行,所以时两个线程对象
执行结果
执行时后睡2s后执行,由一个线程对象执行的
3.2 Executors.newFixedThreadPool();
newFixedThreadPool(10) 这里的值 是线程池的最大值
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)
public class ThreadPool2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.shutdown();
}
}
//获取最大线程池容量
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)
public class ThreadPool2 {
public static void main(String[] args) {
//参数不是初始值 而是最大值
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor pool=(ThreadPoolExecutor) executorService;
System.out.println(pool.getPoolSize());
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.shutdown();
System.out.println(pool.getPoolSize());
}
}
4. 自定义线程池 -ThreadPoolExecutor
代码实现
//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
// BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
// 创建一个新 ThreadPoolExecutor给定的初始参数。
import java.util.concurrent.*;
/***
* 参数1:核心线程数量
* 参数2:最大线程数
* 参数3:空闲线程最大存活时间
* 参数4:时间单位
* 参数5:任务队列 如下最大执行为5个线程,超过5个线程时就要在队列中等待
* 参数6:任务创建工厂 按照默认方式创建线程对象 源码中还是new Thread()
* 参数7:任务拒绝策略 什么时候拒绝任务? 当提交任务>池子中最大线程数量+任务队列容量 怎样拒绝任务??
* 4种拒绝任务策略
*/
public class ThreadPool3 {
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在执行了");
}
}
public static void main(String[] args) {
ThreadPoolExecutor pool=new ThreadPoolExecutor(
2,
5,
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
}
4.1线程池任务拒绝策略
4.1.1 new ThreadPoolExecutor.AbortPolicy()
超过线程池中线程最大容量+任务队列容量时:丢弃任务并抛出异常
代码实现
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/***
* 参数1:核心线程数量
* 参数2:最大线程数
* 参数3:空闲线程最大存活时间
* 参数4:时间单位
* 参数5:任务队列
* 参数6:任务创建工厂
* 参数7:任务拒绝策略
*/
public class ThreadPool4 {
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在执行了");
}
}
public static void main(String[] args) {
ThreadPoolExecutor pool=new ThreadPoolExecutor(
2,
5,
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 16; i++) {
pool.submit(new MyRunnable());
}
pool.shutdown();
}
}
4.1.2 new ThreadPoolExecutor.DiscardPolicy()
直接丢弃 不抛异常
public class ThreadPool4 {
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在执行了");
}
}
public static void main(String[] args) {
ThreadPoolExecutor pool=new ThreadPoolExecutor(
1,
2,
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 1; i <= 5; i++) {
int y=i;
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"*****"+y);
});
}
pool.shutdown();
}
}
执行结果:
4.1.3 new ThreadPoolExecutor.DiscardOldestPolicy()
抛弃队列中等待最久,加入当前任务进入队列
直接放结果
4.1.4 new ThreadPoolExecutor.CallerRunsPolicy()
5. volatile关键字
强制线程每次使用的时候,都会看一下共享区域最新的值
对 volatile 修饰的变量值,保证线程读取到的值是最新的,而不是寄存器中缓存的值。
class Money {
public static volatile int money=100000;
public static void main(String[] args) {
MyThead1 t1=new MyThead1();
MyThead2 t2=new MyThead2();
t2.start();
t1.start();
}
}
class MyThead2 extends Thread{
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Money.money=90000;
}
}
public class MyThead1 extends Thread{
@Override
public void run() {
while (Money.money==100000){
}
System.out.println("结婚基金已经不是10万了");
}
}
5.1 synchronized解决
代码实现:
class Money {
public static volatile int money = 100000;
public static Object lock = new Object();
public static void main(String[] args) {
MyThead1 t1 = new MyThead1();
MyThead2 t2 = new MyThead2();
t2.start();
t1.start();
}
}
class MyThead2 extends Thread {
@Override
public void run() {
synchronized (Money.lock){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Money.money = 90000;
}
}
}
public class MyThead1 extends Thread {
@Override
public void run() {
while (true) {
synchronized (Money.lock) {
if (Money.money != 100000) {
System.out.println("结婚基金已经不是10万了");
break;
}
}
}
}
}
原理:
五、 原子性&&并发工具类
1. 原子性
多个操作是一个不可以分割的整体
volatile不支持原子性
/*
* 当线程A和线程B同时对共享数据操作时,可能A改变了线程栈中副本的值,但是未返回给共享数据时就被线程B
* 抢占了,这时B访问共享数据取到的还是原来的数据,但是当A,B返回共享数据时 实际的操作就出现了不合理
* 也就是 count++ 不是原子性操作
*/
public class MyAtomThread extends Thread {
private int volatile count =0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//1. 从共享数据中读取数据到本线程栈中
//2. 修改本线程栈中变量副本的值
//3. 把本线程栈中变量副本的值赋值给共享数据
count++;
System.out.println("已经送了"+count+"个冰激凌");
}
}
public static void main(String[] args) {
MyAtomThread atom = new MyAtomThread();
for (int i = 0; i < 100; i++) {
new Thread(atom).start();
}
}
}
synchronized具有原子性,但是速度比较慢:都要去判断锁 获得锁 释放锁 的过程
public class MyAtomThread extends Thread {
private int count =0;
private Object lock=new Object();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//1. 从共享数据中读取数据到本线程栈中
//2. 修改本线程栈中变量副本的值
//3. 把本线程栈中变量副本的值赋值给共享数据
synchronized (lock) {
count++;
System.out.println("已经送了"+count+"个冰激凌");
}
}
}
public static void main(String[] args) {
MyAtomThread atom = new MyAtomThread();
for (int i = 0; i < 100; i++) {
new Thread(atom).start();
}
}
}
使用原子类AtomicInteger类实现
import java.util.concurrent.atomic.AtomicInteger;
public class MyAtomThread extends Thread {
private AtomicInteger ac = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//1. 从共享数据中读取数据到本线程栈中
//2. 修改本线程栈中变量副本的值
//3. 把本线程栈中变量副本的值赋值给共享数据
int count=ac.incrementAndGet();
System.out.println("已经送了" + count + "个冰激凌");
}
}
public static void main(String[] args) {
MyAtomThread atom = new MyAtomThread();
for (int i = 0; i < 100; i++) {
new Thread(atom).start();
}
}
}
2. 原子类 AtomicInteger
2.1 AtomicInteger原理:自旋锁+CAS算法
import java.util.concurrent.atomic.AtomicInteger;
public class MyAtomDemo1 {
public static void main(String[] args) {
//初始化一个默认值为0的原子型Integer
AtomicInteger ac=new AtomicInteger();
System.out.println(ac);//0
//初始化一个指定值的原子型Integer
AtomicInteger ac1=new AtomicInteger(10);
System.out.println(ac1);//10
//1. get 获取值
AtomicInteger ac2=new AtomicInteger(10);
System.out.println(ac2.get());//10
//2. getAndIncrement() 以原子方式将当前值+1,注:这里返回的是自增前的值
AtomicInteger ac3=new AtomicInteger(10);
System.out.println(ac3.getAndIncrement());// 还是10
System.out.println(ac3.get());//实际值 +1= 11
//3. incrementAndGet() 以原子方式将当前值+1,注:这里返回的是自增后的值
AtomicInteger ac4=new AtomicInteger(10);
System.out.println(ac4.incrementAndGet());// 变为11
System.out.println(ac4.get());//实际值 11
//4. addAndGet() 以原子方式将参数与对象中的值相加,并返回结果
AtomicInteger ac5=new AtomicInteger(10);
System.out.println(ac5.addAndGet(5));// 15
System.out.println(ac5.get());//实际值 15
//5. addAndGet() 以原子方式设置为newValue的值,并返回旧值
AtomicInteger ac6=new AtomicInteger(10);
System.out.println(ac6.getAndSet(20));// 10
System.out.println(ac6.get());//实际值 20
}
}
3 . synchronized和CAS的区别
4 并发工具类 Hashtable
HashMap线程不安全
import java.util.HashMap;
public class MyHashMapDemo {
public static void main(String[] args) throws InterruptedException {
HashMap<String,String> map=new HashMap<>();
Thread t1=new Thread(()->{
for (int i = 0; i < 25; i++) {
map.put(i+"",i+"");
}
});
Thread t2=new Thread(()->{
for (int i = 25; i < 51; i++) {
map.put(i+"",i+"");
}
});
t1.start();
t2.start();
System.out.println("-----------");
//让main线程睡眠 为了t1t2线程数据添加
//1 2 3 4 5 6
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(map.get(i + ""));
}
}
}
HashTable线程安全
import java.util.Hashtable;
public class MyHashTableDemo {
public static void main(String[] args) throws InterruptedException {
Hashtable<String,String> map=new Hashtable<>();
Thread t1=new Thread(()->{
for (int i = 0; i < 25; i++) {
map.put(i+"",i+"");
}
});
Thread t2=new Thread(()->{
for (int i = 25; i < 51; i++) {
map.put(i+"",i+"");
}
});
t1.start();
t2.start();
System.out.println("-----------");
//让main线程睡眠 为了t1t2线程数据添加
//1 2 3 4 5 6
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(map.get(i + ""));
}
}
}
4.1 Hashtable效率低的原因
5. ConcurrentHashMap 线程安全&效率高
import java.util.concurrent.ConcurrentHashMap;
public class MyConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String,String> map=new ConcurrentHashMap<>();
Thread t1=new Thread(()->{
for (int i = 0; i < 25; i++) {
map.put(i+"",i+"");
}
});
Thread t2=new Thread(()->{
for (int i = 25; i < 51; i++) {
map.put(i+"",i+"");
}
});
t1.start();
t2.start();
System.out.println("-----------");
//让main线程睡眠 为了t1t2线程数据添加
//1 2 3 4 5 6
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(map.get(i + ""));
}
}
}
5.1 ConcurrentHashMap-jdk1.7版本
最多同时16个线程同时执行
5.2 ConcurrentHashMap-jdk1.8版本
1.7需要二次哈希,大数组不能扩充
ctrl+shift+alt+u看源码结构
6. CountDownLatch的使用
使用场景:让一条线程等待其他线程执行完毕后再执行
控制线程运行额类
主要方法
import java.util.concurrent.CountDownLatch;
class ChildThread1 extends Thread {
public CountDownLatch countDownLatch;
public ChildThread1(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.吃饺子
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
}
//2. 吃完汇报
countDownLatch.countDown();
}
}
class ChildThread2 extends Thread {
public CountDownLatch countDownLatch;
public ChildThread2(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.吃饺子
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
}
//2. 吃完汇报
countDownLatch.countDown();
}
}
class ChildThread3 extends Thread {
public CountDownLatch countDownLatch;
public ChildThread3(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.吃饺子
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
}
//2. 吃完汇报
//当每次调用countDown时 计数器就会-1
countDownLatch.countDown();
}
}
class MotherThread extends Thread {
public CountDownLatch countDownLatch;
public MotherThread(CountDownLatch countDownLatch) {
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//1.等待
try {
//当计数器变为0时,会自动唤醒等待的线程
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.收拾碗筷
System.out.println(Thread.currentThread().getName()+"准备收拾碗筷");
}
}
public class MyCountDownLatchDemo {
public static void main(String[] args) {
//1. 创建CountDownLatch的对象,此处需要传递四个线程
//在底层实现了一个计数器,此时计数器的值是3
CountDownLatch countDownLatch=new CountDownLatch(3);
//创建4个线程并全部开启
MotherThread m=new MotherThread(countDownLatch);
m.setName("妈妈");
m.start();
ChildThread1 c1=new ChildThread1(countDownLatch);
ChildThread2 c2=new ChildThread2(countDownLatch);
ChildThread3 c3=new ChildThread3(countDownLatch);
c1.setName("小红");
c2.setName("小蓝");
c3.setName("小黑");
c3.start();
c1.start();
c2.start();
}
}
7. Semaphore的使用
使用场景:可以控制访问特定资源的线程数量
import java.util.concurrent.Semaphore;
class MyRunnable implements Runnable {
private Semaphore semaphore;
public MyRunnable(Semaphore semaphore) {
this.semaphore = semaphore;
}
//1.获得管理员对象
@Override
public void run() {
//2.获得通行证
try {
semaphore.acquire();
//3.开始通过
System.out.println("已获得通行证,正在通行ing");
Thread.sleep(2000);
System.out.println("归还通行证");
//4.归还通行证
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MySemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); //最多通行几条线程
MyRunnable myRunnable = new MyRunnable(semaphore);
for (int i = 0; i < 10; i++) {
new Thread(myRunnable).start();
}
}
}