买票问题复习
- 高内聚 低耦合前提下, 线程操作资源类
- 判断/干活/通知
- 多线程交互中, 必须要防止多线程的虚假唤醒(spurious wakeup), 也即(判断必须要用
while
, 不能用if
). - 注意标志位的修改和定位.
IDEA中要生成自己的代码块的快捷键!!!
给IDEA增加自定义的代码模板: Idea代码模板和自定义代码模板的使用
精确通知顺序访问
package test;
/*
* 多线程之间按顺序调用,实现A->B->C
* 三个线程启动,要求如下:
*
* AA打印5次, BB打印10次, CC打印15次
* 接着
* AA打印5次, BB打印10次, CC打印15次
* 。。。。。来10轮
* */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//首先写一个资源类
class ShareResources {
private int whoToPrint = 1; // 1: A 2: B, 3: C
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5() {
lock.lock();
try {
//1. 判断
while (whoToPrint != 1) {
condition1.await();
}
//2. 干活
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "AA");
}
//3. 通知
whoToPrint = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10() {
lock.lock();
try {
//1. 判断
while (whoToPrint != 2) {
condition2.await();
}
//2. 干活
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "BB");
}
//3. 通知
whoToPrint = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() {
lock.lock();
try {
//1. 判断
while (whoToPrint != 3) {
condition3.await();
}
//2. 干活
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "CC");
}
//3. 通知
whoToPrint = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResources shareResources = new ShareResources();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
shareResources.print5();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
shareResources.print10();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
shareResources.print15();
}
}, "C").start();
}
}
八锁
You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the
Class
object associated with the class. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.
集合不安全
LIst不安全
UUID 和 System.currentTimeMillis()的使用, 生成不重复的字符串
问题:
java.util.ConcurrentMethodModificationException
解决方法:
Vector
Collections.synchronizedList()方法
java.util.concurrent.CopyOnWriteArrayList
类
是一种读写分离的思想,读和写不同的容器
//java.util.concurrent.CopyOnWriteArrayList中的add方法源码
public boolean add(E e) {
synchronized(this.lock) {
Object[] es = this.getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
this.setArray(es);
return true;
}
}
Set不安全
和List报一样的错
解决方法:
Collections.synchronizedSet()方法
java.util.concurrent.CopyOnWriteArraySet
Map不安全
解决方法:
Collections.synchronizedMap()方法
java.util.concurrent.ConcurrentHashMap
多线程中第三种获得多线程的方式----Callable
package test;
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.println(Thread.currentThread().getName() + "***********come in here");
return 1024;
}
}
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask futureTask = new FutureTask(new MyThread());
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();
System.out.println(Thread.currentThread().getName() + "******计算完成");
System.out.println(futureTask.get());
}
}
运行结果
注意:
FutureTask
的get
方法要放在最后- 如果两个线程初始化用的是同一个
FutureTask
, 那么call()
方法只会执行一次.
java.util.concurrent.CountDownLatch
class
package test;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t班长关门走人");
}
}
输出结果
java.util.concurrent.CyclicBarrier
class
package test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("**********召唤神龙");
});
for (int i = 0; i < 7; i++) {
int finalI = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t收集到第" + finalI + "颗龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}, String.valueOf(i)).start();
}
}
}
输出结果:
java.util.concurrent.Semaphore
class
package test;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t抢占到了车位");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
输出结果:
java.util.concurrent.locks.ReadWriteLock
interface
BEFOR
package test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/*
* 多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行
* 但是
* 如果有一个线程想去写共享资源类,就不应该有其他线程可以对该资源列进行读或者写
* 小总结:
* 读-读 能共存
* 读-写 不能共存
* 写-写 不能共存
* */
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "\t 开始写入数据" + key);
try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成");
}
void get(String key) {
System.out.println(Thread.currentThread().getName() + "\t ----开始读取数据");
try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t ----读取完成" + result);
}
}
public class ReadWrieteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.get(finalI +"");
}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.put(finalI +"", finalI +"");
}, String.valueOf(i)).start();
}
}
}
输出结果:
AFTER
package test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
* 多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行
* 但是
* 如果有一个线程想去写共享资源类,就不应该有其他线程可以对该资源列进行读或者写
* 小总结:
* 读-读 能共存
* 读-写 不能共存
* 写-写 不能共存
* */
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 开始写入数据" + key);
try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t ----开始读取数据");
try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t ----读取完成" + result);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
public class ReadWrieteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.get(finalI +"");
}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.put(finalI +"", finalI +"");
}, String.valueOf(i)).start();
}
}
}
输出结果:
阻塞队列java.util.concurrent.BlockingQueue
阻塞队列
阻塞队列的用处
阻塞队列的种类分类
BlockingQueue核心方法
线程池
为什么要用线程池?
Executor框架的组织架构
基本用法概览
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();//一池N个工作线程,类似与一个银行有N个受理窗口
//ExecutorService threadPool = Executors.newFixedThreadPool(5);//一池5个工作线程,类似与一个银行有5个受理窗口
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//一池1个工作线程,类似与一个银行有1个受理窗口
try {
for (int i = 0; i < 10; i++) {
//try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
threadPool.execute(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t办理业务");
});
}
}catch (Exception e) {
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
运行结果
线程池的7个参数
请看ThreadPoolExecutor
类的构造方法的源码:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
this.mainLock = new ReentrantLock();
this.workers = new HashSet();
this.termination = this.mainLock.newCondition();
if (corePoolSize >= 0 && maximumPoolSize > 0 && maximumPoolSize >= corePoolSize && keepAliveTime >= 0L) {
if (workQueue != null && threadFactory != null && handler != null) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
} else {
throw new NullPointerException();
}
} else {
throw new IllegalArgumentException();
}
}
七个参数的解释:
线程池的工作原理
线程池的拒绝策略
JDK有四种内置策略,这四种内置策略均实现了RejectedExecutionHandle
接口
这四种内置策略是:
package test;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(2,
5,
2L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
try {
for (int i = 0; i < 10; i++) {
int finalI = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t正在办理业务" + finalI);
});
}
}catch (Exception e) {
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
运行结果:
- 在cpu密集型的任务中,自定义线程池时,最大线程数是cpu的核数+1,可以用
Runtime.getRuntime().availableProcessors()
获取cpu的核数.- 在io密集型任务中,由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*CPU数目。
链式编程, 流式计算
java.util.function
内置的四大函数式接口
ForJoinDemo
package test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
class MyTask extends RecursiveTask<Integer> {
private static final Integer ADJUST_VALUE = 10;
private int begin;
private int end;
private int result;
MyTask(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
protected Integer compute() {
if ((end-begin) <= ADJUST_VALUE) {
for (int i = begin; i <= end; i++) {
result = result + i;
}
}else{
int middle = (begin + end) / 2;
MyTask task01 = new MyTask(begin, middle);
MyTask task02 = new MyTask(middle + 1, end);
task01.fork();
task02.fork();
result = task01.join() + task02.join();
}
return result;
}
}
public class ForkJoinDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask myTask = new MyTask(0, 100);
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
System.out.println(forkJoinTask.get());
forkJoinPool.shutdown();
}
}
输出结果: 5050
异步调用java.util.concurrent.CompletableFuture
类
package test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName() + "没有返回值,update mysql ok");
});
//try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(completableFuture.get());
System.out.println("****************************************");
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t 有返回值的");
int age = 10/0;
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return 1024;
});
System.out.println(
completableFuture1.whenCompleteAsync((t, u) -> {
System.out.println("******t: " + t);
System.out.println("******u: " + u);
}).exceptionally((f) -> {
System.out.println(f.getMessage());
return 4444;
}).get()
);
}
}
输出结果
Serializable
接口, ·Cloneable·接口
什么是lombok注解?