1. 用户线程与守护线程
public class test1 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon()); // 用户线程不会随着主线程关闭
while(true){
}
},"ydy");
thread.setDaemon(true); // 设置为守护线程 守护线程会随着主线程的结束而结束
thread.start();
System.out.println("main over");
}
}
## 2 . Synchronized 同步锁
// 模拟三个窗口同时进行售票
// 上锁以及解锁过程为自动实现
public class test2 {
static class Ticket {
private Integer sum;
public Ticket(Integer sum) {
this.sum = sum;
}
// 每次卖出一张票
public synchronized void buy(){
if(sum > 0) {
sum --;
System.out.println(Thread.currentThread().getName() + ":: 售出");
}
}
}
public static void main(String[] args) throws InterruptedException {
Ticket ticket = new Ticket(50);
// Initialize threads
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i < 30;i ++) ticket.buy();
}
}, "1").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i < 30;i ++) ticket.buy();
}
}, "2").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i < 30;i ++) ticket.buy();
}
}, "3").start();
}
}
3 . Lock接口
static class Ticket {
private Integer sum;
Lock lock = new ReentrantLock();
public Ticket(Integer sum) {
this.sum = sum;
}
// 每次卖出一张票
public void buy(){
lock.lock();
try{
if(sum > 0) {
sum --;
System.out.println(Thread.currentThread().getName() + ":: 售出");
}
}finally {
lock.unlock();
}
}
}
4 . 线程间通信
// 多个线程操作同一个变量
// 使用 synchronized 实现
public class test3 {
static class Share{
private int number = 0;
public synchronized void add() throws InterruptedException {
while(number != 0) this.wait();
number ++;
System.out.println(Thread.currentThread().getName() + "::" + "add");
notifyAll();
}
public synchronized void dec() throws InterruptedException {
while(number != 1) this.wait();
number --;
System.out.println(Thread.currentThread().getName() + "::" + "dec");
notifyAll();
}
}
public static void main(String[] args) {
Share share = new Share();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i <= 10;i ++){
try {
share.add();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
share.dec();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}, "BB").start();
}
}
如果使用的是if 判断的话,可能存在虚假唤醒问题,所以这里使用while循环判断。
// 使用 Lock 接口实现
static class Share{
private int number = 0;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void add() throws InterruptedException {
lock.lock();
try{
while(number != 0) condition.await();
number ++;
System.out.println(Thread.currentThread().getName() + "::" + number);
condition.signalAll();
}finally {
lock.unlock();
}
}
public void dec() throws InterruptedException {
lock.lock();
try{
while(number != 1) condition.await();
number --;
System.out.println(Thread.currentThread().getName() + "::" + number);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
5 . 线程间定制性通信
// 实现先 打印 A 然后是 BB 之后是 CCC
static class Share{
private int flag = 1;
private final Lock lock = new ReentrantLock();
private final Condition a = lock.newCondition();
private final Condition b = lock.newCondition();
private final Condition c = lock.newCondition();
public void printA(){
lock.lock();
try {
while(flag != 1) a.await();
for (int i = 0; i < 1; i++) {
System.out.println("A");
}
flag = 2;
b.signal(); // 通知下一个要操作的线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while(flag != 2) b.await();
for (int i = 0; i < 2; i++) {
System.out.println("B");
}
flag = 3;
c.signal(); // 通知下一个要操作的线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while(flag != 3) c.await();
for (int i = 0; i < 3; i++) {
System.out.println("C");
}
flag = 1;
a.signal(); // 通知下一个要操作的线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
6 . 集合线程安全
ArrayList
注意:ArrayList是线程不安全的,源码内部没有加锁
多个线程同时存放数据以及获取数据就会发生并发修改异常
解决方案 1 Vector
注意 : 该方法比较古老,现在很少使用
直接用Vector实现类替代ArrayList即可
Vector源码中,其内部方法是加锁的。 Synchronized
解决方案 2 Collections
注意:这个也是古老方法,可能会报错
Collections.synchronizedList(new ArrayList<>());
常用 解决方案 3
List<Object> objects = new CopyOnWriteArrayList<>();
7. HashSet与HashMap的线程安全问题
与ArrayList相同,会出现并发修改访问问题
Set<String> set = new CopyOnWriteArraySet<>(); Map<String, String> hashMap = new ConcurrentHashMap<>();
8.Synchronized 锁的八种情况
9 . 公平锁与非公平锁
公平锁: 阳光普照 但是效率较低
非公平锁: 效率较高,但是可能出现线程饿死问题
Lock lock = new ReentrantLock(true); // 创建公平锁 不加参数默认是非公平锁
// 源码
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
10 . 可重入锁 (递归锁)
synchronized 与 lock 都是可重入锁
简单来说就是在锁内部区域内的代码片段都可以正常访问,不会因为锁而无法访问
public static synchronized void add(){ // 该方法会出现栈溢出异常
add();
}
11 . 死锁
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
详解链接
死锁面试题(什么是死锁,产生死锁的原因及必要条件)-CSDN博客
12 . Callable接口
FutureTask<Integer> futureTask = new FutureTask<>(() ->{
return 1024;
});
new Thread(futureTask).start();
System.out.println(futureTask.get());
// 注意 : 当该线程计算完返回值之后,那么下一次调用get方法就不需要等待,直接将之前计算得到的返回值返回。
13. 辅助类
CountDownLatch 减少计数
例如 : 六个打扫教室的同学都离开教室之后,才可以关门。
CountDownLatch countDownLatch = new CountDownLatch(6);
for(int i = 0;i < 6; i++){
new Thread(() ->{
System.out.println( Thread.currentThread().getName() + " 号同学离开了");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("关门");
CyclicBarrier 循环栅栏
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() ->{
System.out.println("七龙珠收集完毕 ----");
});
for (int i = 0; i < 7; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "号 收集到了");
cyclicBarrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
Semaphore 信号灯
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 抢占车位
System.out.println(Thread.currentThread().getName() + "抢占了车位");
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName() + "-- 离开了车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
14. 读写锁
读锁:共享锁
写锁:独占锁
二者都可能发生死锁现象
// 多线程对于Map集合进行操作
static class MyMap{
public volatile Map<String, Object> map = new HashMap<>();
public volatile ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void put(String key,Object val){
try{
lock.writeLock().lock();
System.out.println(key + "----开始写数据");
TimeUnit.SECONDS.sleep(5);
System.out.println(key + "----::数据写完了");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.writeLock().unlock();
}
}
public void get(String key){
try {
lock.readLock().lock();
System.out.println("----开始读数据" + key);
TimeUnit.SECONDS.sleep(5);
System.out.println("----::数据读完了" + key);
} catch (InterruptedException e) {
}finally {
lock.readLock().unlock();
}
}
}
public static void main(String[] args) {
MyMap map = new MyMap();
for(int i = 0;i < 5;i ++){
final int num = i;
new Thread(() -> {
map.put(String.valueOf(num),num);
},String.valueOf(num)).start();
}
for(int i = 0;i < 5;i ++){
final int num = i;
new Thread(() -> {
map.get(String.valueOf(num));
},String.valueOf(num)).start();
}
}
演变
降级
15.阻塞队列 BlockingQueue
16.线程池
在执行一个异步任务或并发任务时,往往是通过直接new Thread()方法来创建新的线程,这样做弊端较多,更好的解决方案是合理地利用线程池,线程池的优势很明显,如下:
降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗; 提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行; 方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而 降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率; 更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。
使用方式:
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 10; i++) {
executorService2.execute(() -> {
System.out.println(Thread.currentThread().getName() + ":: 执行");
});
}
}finally {
executorService2.shutdown(); // 线程回收
}
// 七个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
工作流程
自定义线程池
// 自定义线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
10, 60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
17. 分支合并框架
类似于递归 拆分为子问题
static class MyTask extends RecursiveTask<Integer> {
private Integer b = 10;
private Integer l,r;
public MyTask(Integer l,Integer r){
this.l = l;
this.r = r;
}
@Override
protected Integer compute() {
int res = 0;
if(r - l <= b){
for(int i = l;i <=r ;i++) res += i;
}else {
int mid = (l + r) / 2;
MyTask t1 = new MyTask(l,mid);
MyTask t2 = new MyTask(mid + 1,r);
t1.fork(); t2.fork();
res = t1.join() + t2.join();
}
return res;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask task = new MyTask(0,20);
// 创建分支合并池对象
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(task);
// 获取分支合并结果
Integer ans = forkJoinTask.get();
System.out.println(ans);
// 关闭池对象
forkJoinPool.shutdown();
}
18. 异步回调
CompletableFuture<Void> e1 = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
});
e1.get();
CompletableFuture<Integer> e2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
return 1;
});
e2.whenComplete((t,u) -> { // 返回值t 以及 运行异常u
System.out.println(t + "::" + u);
});