JUC初步
作者:李晶晶;
日期:2021年4月20日;
文章目录
初识Lock
抢票案例
package 一.初识lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 原来都是在买票的方法上加上synchronized关键字保证线程同步; 我们使用JUC中的lock来做也可以实现,而且效率更高;
*
* @author 木子六日
*
*/
public class Ticket {
private int number = 20;
private Lock lock = new ReentrantLock();
public void saleTicket() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + (--number));
}
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.saleTicket();
}
}, "售票员甲").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.saleTicket();
}
}, "售票员乙").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
ticket.saleTicket();
}
}, "售票员丙").start();
}
}
生产者消费者问题
使用Lock解决生产者消费者问题
package 二.新版生产者消费者;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用lock实现生产者消费者模型
*
* @author 木子六日
*
*/
public class Resource {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() {
lock.lock();
try {
while (count > 0) {
condition.await();
}
count++;
System.out.println(Thread.currentThread().getName() + "\t" + count);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
while (count == 0) {
condition.await();
}
count--;
System.out.println(Thread.currentThread().getName() + "\t" + count);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.decrement();
}
}, "消费者1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.decrement();
}
}, "消费者2").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
resource.increment();
}
}, "生产者1").start();
}
}
Condition的精准唤醒
轮流打印案例
package 三.轮流打印;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author 木子六日
*/
public class ShareResource {
private int tag = 0;
private Lock lock = new ReentrantLock();
private List<Condition> conditions = new ArrayList<Condition>();
public ShareResource() {
for (int i = 0; i < 3; i++) {
conditions.add(lock.newCondition());
}
}
public void printName(int i) {
lock.lock();
try {
while (tag != i) {
conditions.get(i).await();
}
tag = (i + 1) % 3;
System.out.println(Thread.currentThread().getName());
conditions.get(tag).signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ShareResource resource = new ShareResource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printName(0);
}
}, "甲").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printName(1);
}
}, "乙").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printName(2);
}
}, "丙").start();
}
}
线程安全的集合类
线程安全的List
package 四.线程安全的集合类;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
public class SecureList {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
List<Integer> a = new ArrayList<Integer>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
a.add(0);
}, "ArrayList").start();
}
// 线程安全,就是给add方法加了syn
List<Integer> b = new Vector<Integer>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
b.add(0);
}, "Vector").start();
}
// 还可以这样保证线程安全
List<Integer> c = Collections.synchronizedList(new ArrayList<Integer>());
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
c.add(0);
}, "synchronizedList").start();
}
// JUC提供的线程安全list
List<Integer> d = new CopyOnWriteArrayList<Integer>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
d.add(0);
}, "CopyOnWriteArrayList").start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(a.size());
System.out.println(b.size());
System.out.println(c.size());
System.out.println(d.size());
}
}
线程安全的Set
package 四.线程安全的集合类;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
public class SecureSet {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
Set<String> a = new HashSet<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
a.add(UUID.randomUUID().toString());
}, "HashSet").start();
}
// 第一种解决方案
Set<String> b = Collections.synchronizedSet(new HashSet<String>());
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
b.add(UUID.randomUUID().toString());
}, "synchronizedSet").start();
}
// JUC的解决方案
Set<String> c = new CopyOnWriteArraySet<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
c.add(UUID.randomUUID().toString());
}, "CopyOnWriteArraySet").start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(a.size());
System.out.println(b.size());
System.out.println(c.size());
}
}
线程安全的Map
package 四.线程安全的集合类;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class SecureMap {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
Map<String, String> a = new HashMap<String, String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
a.put(UUID.randomUUID().toString(), "0");
}, "HashMap").start();
}
// Hashtable,类似Vector,就是给put方法加了syn
Map<String, String> b = new Hashtable<String, String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
b.put(UUID.randomUUID().toString(), "0");
}, "Hashtable").start();
}
// 这个是万能的
Map<String, String> c = Collections.synchronizedMap(new HashMap<String, String>());
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
c.put(UUID.randomUUID().toString(), "0");
}, "Hashtable").start();
}
// JUC的解决方案
Map<String, String> d = new ConcurrentHashMap<String, String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
d.put(UUID.randomUUID().toString(), "0");
}, "Hashtable").start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(a.size());
System.out.println(b.size());
System.out.println(c.size());
System.out.println(d.size());
}
}
Callable接口
Callable与FutureTask
package 五.Callable接口;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class Mythread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.print("icu");
return 996;
}
}
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Mythread());
futureTask.run();
System.out.println(futureTask.get());
// FutrueTask实现了runnable接口,所以也可以使用传统方式执行线程
new Thread(futureTask, "A").start();
// futureTask只会执行一次,因为里面封装了线程的状态,线程状态不是new的话就直接return了
}
}
JUC中的一些工具
CountDownLatch
package 六.JUC的一些工具;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
countDownLatch.countDown();
}, i + "").start();
}
countDownLatch.await();
System.out.println("main");
}
}
CyclicBarrier
package 六.JUC的一些工具;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> System.out.println("main"));
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, i + "").start();
}
}
}
Semaphore
package 六.JUC的一些工具;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
private static int resource = 3;
private static Random r = new Random();
public static void main(String[] args) {
System.out.println("共有5份资源");
Semaphore semaphore = new Semaphore(resource);
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "占有了资源");
TimeUnit.SECONDS.sleep(r.nextInt(5) + 1);
System.out.println(Thread.currentThread().getName() + "释放了资源");
} catch (Exception e) {
} finally {
semaphore.release();
}
}, "车子" + i).start();
}
}
}
读写锁
使用案例
package 七.读写锁;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 可以读读,不能读写、写写
*
* @author 木子六日
* @date 2021年3月23日
*/
class MyCache {
private Map<Integer, String> cache = new HashMap<Integer, String>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void get(Integer k) {
try {
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "开始读取数据");
String res = cache.get(k);
System.out.println(Thread.currentThread().getName() + "读取完成,数据是:" + res);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
public void put(Integer k, String v) {
try {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "开始写入数据");
cache.put(k, v);
System.out.println(Thread.currentThread().getName() + "写入数据" + v + "成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache cache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int k = i;
new Thread(() -> {
cache.put(k, UUID.randomUUID().toString());
}, "写线程" + i).start();
}
for (int i = 1; i <= 5; i++) {
final int k = i;
new Thread(() -> {
cache.get(k);
}, "读线程" + i).start();
}
}
}
阻塞队列
put与take
package 八.阻塞队列;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 原本的那些add、remove、element、offer、poll、peek队列方法照用 增加了put和take方法
*
* @author 木子六日
* @date 2021年4月20日
*/
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(5);
new Thread(() -> {
while (true) {
try {
// 队列满了put将被阻塞
blockingQueue.put("bread");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产:" + blockingQueue.size());
}
}, "生产者").start();
new Thread(() -> {
while (true) {
// 队列空了poll将被阻塞
blockingQueue.poll();
System.out.println("消费:" + blockingQueue.size());
}
}, "消费者").start();
}
}
线程池
使用Executors创建线程池
package 九.线程池;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 设置线程池大小为3
// ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 一个线程池里就只有一个线程
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 动态扩容的
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
threadPool.execute(() -> System.out.println(Thread.currentThread().getName()));
}
// 需要关闭线程池
threadPool.shutdown();
}
}
ThreadPoolExecutor分析
package 九.线程池;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 分析一下ThreadPoolExecutor的七大参数
* @author 木子六日
* @date 2021年4月20日
*/
public class ThreadPoolExecutorAnalysis {
//这里先粘一段源码
/*
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
*/
/*
无论是Executors.newFixedThreadPool(3)
还是Executors.newSingleThreadExecutor()
或者Executors.newCachedThreadPool()
他们都是返回的一个ThreadPoolExecutor的不同构造而已
所以线程池就是ThreadPoolExecutor
*/
/*
corePoolSize:
常驻核心线程数,永远在的
maximumPoolSize:
最大线程数,阻塞队列满了,核心线程还没释放,这时候又来任务了,放不下了,就开始扩容,直至最大线程数
keepAliveTime:
线程存续时间,假如超过了这个时间,而且并没有再用到那么多的线程,超出corePoolSize部分的线程会被销毁
unit:
keepAliveTime的时间单位
workQueue:
一个阻塞队列,待执行的任务
threadFactory:
线程工厂,用于创建线程的,一般用默认的就好,不必改动
handler:
拒绝策略,阻塞队列满了,而且已经开到最大线程数了,解不了活了,执行拒绝策略
*/
/*
《阿里巴巴Java开发手册》里面不允许使用Executors工具类来创建线程池
因为FixedThreadPool和SingleThreadPool的任务队列最大是Integer.MAX_VALUE
而CacheThreadPool和ScheduledThreadPool的线程数量最大是Integer.MAX_VALUE
它们都有可能OOM
*/
public static void main(String[] args) {
//CPU核心数
int num = Runtime.getRuntime().availableProcessors();
System.out.println(num);
//对于cpu密集型的任务,一般都会将最大线程数设置为cpu核心数+1
ExecutorService threadPool = new ThreadPoolExecutor(2, num + 1, 2L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3));
}
/*
线程开到最大数量并且任务队列满了,又来了新任务,这时候就要使用拒绝策略了
四大拒绝策略:
AbortPolicy:
这是默认的拒绝策略,直接给我爬,抛异常
CallerRunPolicy:
回退,哪个线程让你来的,你回去那让个线程执行你这个任务去
DiscardPolicy:
拒绝你了还没叼你,你都不知道自己被拒绝了,也不抛异常
DiscardOldestPolicy:
不抛弃你,把队列里等的时间最长的抛弃,喜新厌旧
*/
}