一. 集合安全类的使用
List list2 = new ArrayList<>(); // 线程不安全类
List list3 = Collections.synchronizedList(list2); // 工具类使其安全
Collections.synchronizedMap();
List list = new CopyOnWriteArrayList(); // JUC 的安全方法
new CopyOnWriteArraySet<>();
new ConcurrentHashMap<>();
二. 多线程
1.1 传统的 synchronized
this.wait();
this.notifyAll(); (用this调用,synchronized锁的是对象 , 勿写成Thread.currentThread().wait())
1.2 轻便的 lock
private Lock lock = new ReentrantLock(); 制造一个锁
private Condition condition1 = lock.newCondition(); 可以控制精确唤醒
condition1.await(); condition1.signalAll();
1.3
三. 多线程下的接口
1.多线程下返回值--FutureTask 与 Callable
1.1 实现接口,重写方法。 例
class testMythread implements Callable<String>
1.2 启动线程。 例
testMythread tm = new testMythread();
FutureTask ft = new FutureTask(tm);
new Thread(ft, "a").start(); // 放进你被包裹的task
System.out.println(ft.get()); // 从中拿到返回值
2. 线程安全的计数器 CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(5); // 放入次数---5
for (int i = 0; i < 5; i++) {
System.out.println("写你的业务...");
countDownLatch.countDown(); // -1
}
countDownLatch.await(); // 0
System.out.println("计数器清零后 执行以下...");
3. 线程到达已经临界值,触发方法,CyclicBarrier(相当于计数器增加版)
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("5次满了,执行该输出"));
for (int i = 0; i < 8; i++) {
new Thread(()->{ System.out.println("业务块...");
cyclicBarrier.await(); // 为了简洁,未处理异常
} , "线程"+new Random(10)).start();
}
4. 每次最多有几个信号量线程 Semaphore
Semaphore smp = new Semaphore(3); // 最多3个线程
for (int i = 0; i < 5; i++) { // 创建5线程,
new Thread(() -> {
try {
smp.acquire(); // 获得一个信号量,(如果满了,就一直等待)
System.out.println("你的业务块...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
smp.release(); // 释放了该信号量
}
}).start();
5. 读写锁 允许多个线程能同时读取共享资源。但是每次只有一个线程能去写这些共享资源, 结构举例:
static Map<String, Object> map = new HashMap<String, Object>();
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock read = rwl.readLock();
static Lock write = rwl.writeLock();
// 获取一个key对应的value
public static final Object get(String key) {
read.lock();
try {
业务
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
read.unlock();
}
写锁结构一样,省略...
6. 阻塞队列 BlockingQueue
BlockingQueue<Object> queue = new ArrayBlockingQueue<>(3); // 容量3 ,
queue.add(1); // add remove element(获取第一个元素) 这组抛异常
queue.add(2); //对应 offer poll peek 这组不抛异常
queue.add(3);
// queue.add(4); //抛异常
queue.remove(3); // 移除
queue.add(4);
queue.element();
queue.poll();
queue.offer(5);
queue.peek();
System.out.println( );
7. 同步队列 SynchronousQueue
该队列必须由多个线程完成,比如1线程存,那就必须2线程取。不能1线程取。
值得一提的是1存了,2取出来, 1再存 ,存不上(必须2再take一下, 似乎才能回到1线程,继续存)
例如:
SynchronousQueue synchronousQueue = new SynchronousQueue();
new Thread(()-> {
try {
synchronousQueue.put(1);
System.out.println("put-----");
synchronousQueue.put(2);
System.out.println("put2-----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "name1").start();
new Thread(()-> {
try {
synchronousQueue.take();
System.out.println("take---");
synchronousQueue.take();
System.out.println("take2---");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "name2").start();
8. 线程池技术
规约:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式, 以便用者根据实际,构造细节。
系统的Executors更大,容易浪费内存
8.1 Executors
// ExecutorService pool = Executors.newSingleThreadExecutor(); // 创建单个线程
// ExecutorService pool = Executors.newFixedThreadPool(3); // 创建3个线程
ExecutorService pool = Executors.newCachedThreadPool(); // 自适应,遇强则强,遇弱则弱
for (int i = 0; i < 5000000; i++) {
pool.execute(()-> System.out.println(Thread.currentThread().getName() + "---"));
}
pool.shutdown();
8.2 ThreadPoolExecutor
主要就是构造函数,加上了手动写参数
重点注意最后一个参数
/**
* new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!
*/
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5, 3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(),
Executors.defaultThreadFactory());
for (int i = 0; i < 50000; i++) {
threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "---"));
}
threadPool.shutdown();
四. lambda(拉姆达)
/*
- Java8 内置的四大核心函数式接口
- Consumer : 消费型接口
-
void accept(T t);
- Supplier : 供给型接口
-
T get();
- Function<T, R> : 函数型接口
-
R apply(T t);
- Predicate : 断言型接口
-
boolean test(T t);
*/
五. ForkJoin
在 JDK 1.7 , 并行执行任务!提高效率。大数据量!大数据:Map Reduce (把大任务拆分为小任务)
1.1 实现接口,例public class ForkJoinDemo extends RecursiveTask<Long>
1.2 覆写 compute() ,由于里面的递归结构,所以必须有拆到最后的计算办法
比如以下 if, 后面得else为拆分
@Override
protected Long compute() {
if ((end-start)<1000){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
long middle = (start + end) / 2; // 中间值
forkJoin task1 = new forkJoin(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
forkJoin task2 = new forkJoin(middle + 1, end);
task2.fork(); // 拆分任务,把任务压入线程队列
return task1.join() + task2.join();
}
}
2.1 stream流计算(最快)
例如上面的例子,也可以 long sum = LongStream.rangeClosed(0L, 1000_0000L).parallel().reduce(0, Long::sum);
六. JMM Java内存模型,不存在的东西,概念!约定!
- Volatile 是 Java 虚拟机提供轻量级的同步机制
1、保证可见性 对一个变量的修改会立即通知到其他线程上 2、不保证原子性 3、禁止指令重排
对于单线程而言,指令重排对结果无影响。 但是多线程中,会发生影响。 举例子: 如果一个线程初始化a,b的值。 另一个线程只要拿到b的值,就输出a。 那你想想,重排后,必然影响
```
- 关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存。
2、线程加锁前,必须读取主存中的最新值到工作内存中
3、加锁和解锁是同一把锁
-
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量 才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机 遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变 量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中, 以便后续的write使用其中规则 和 图
七. 一些概念
1. cas
CAS的全称是Compare And Swap 即比较和交换, 乐观锁的一种实现,乐观锁每次比较版本有没有变化,没有就认为没操作过。
一说为compareAndSet
2. ABA问题
当把一个值改为新值,下一次操作又改回最初的值, 这时候无法辨认是否操作过, 加入了版本(每次都加一个版本),解决了该问题
3.
公平锁: 不能够插队,必须先来后到
非公平锁: 可以插队 (默认都是非公平)
4. 可重入锁
假设两方法上面都有锁,一个方法内调用另一个方法, 那么线程掉第一个方法时候,会一次拿到所有的锁。
5. 自旋锁,就是一直while循环 直到符合条件
例 你可以一个线程修改值, 另一个无限循环,直到第一个线程把值改为你符合的值,你就执行
while (!atomicReference.compareAndSet(null,thread)){
}
6. 死锁
6.1 jps -l, 服务器运行中,可以使用 定位进程号 ,然后看见有哪些进程占用那么高,有点异常
6.2 jstack 进程号, 找到死锁问题,该指令可以定位到哪行代码