启动线程的只有一个,代理模式Thread(proxy).start();
Runnable和Callable只能以实例的形式被代理启动线程。
override run();
//运行 .start()
没有返回值,不会抛出异常
override run();
//运行 需要代理类对象运行 new Thread(实现类).start
类似于Runnable,都是为其实例可能有另一个线程执行的类设计的
有返回值,会抛出异常
override call();
return
//运行方式1
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
public class FutureTask<V> implements RunnableFuture<V>
public FutureTask(Callable<V> callable)
new Thread(new Runnable()).start();
new Thread(new FutureTask<V>(),"A").start();//
new Thread(new FutureTask<V>(),"B").start();//有缓存,只执行1次call(),
futureTask .get();//可能会产生阻塞,放到代码最后,或者使用异步通信处理,跑完之后在拿结果
new Thread(new FutureTask<V>(Callable).start();
FutureTask futureTask = new FutureTask(Callable callable);
FutureTask继承了Runnable接口,运行需要代理类对象运行 new Thread(实现类).start
//运行方式2
executorService.submit((Callable callable);
5个:1.新生状态 2.就绪状态 3.运行状态 4.阻塞状态 5.死亡状态
不建议使用jdk提供的stop(),destory(),已废弃,使用标识控制线程停止
线程阻塞,参数时间,存在异常需要捕获,休眠后会进入就绪状态,每一个对象都有一个锁,sleep不会释放锁
当前正在执行的线程暂停,线程从运行状态转为就绪状态,不阻塞,也不一定礼让成功
插队的意思,当前线程阻塞,执行其他线程,其他线程执行完后在执行当前线程
枚举类型,有6个
不一定成功,但是会增加成功的权重
线程分为用户线程和守护线程,虚拟机必须确保用户线程执行完毕,而守护线程不需要等待执行完毕
多个线程操作同一个数据的时候在同一时刻会产生并发,需要同步,最可靠的保证办法就是同步(排队),此时这些线程就进入对象的等待池,形成队列,排队执行
每个对象都有一把锁,这个锁就是在线程同步时候使用,线程同步形成的条件 :队列+锁
为了保证数据的安全性使用锁,synchronized,排他锁,独占资源,其他线程必须等待,所以也存在一些问题
1.其他需要此锁的线程挂起等待,效率变低
2.在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,必然会损失性能
3.优先级高的线程等待优先级低的线程释放锁,会引起优先级倒置,引起性能问题(重要):比如说Thread A run()10H,Thread B run()1S
B要等待10小时
多个线程相互持有对方需要的资源,形成僵持
形成条件(4个同时满足)
互斥,一个资源每次只能被一个进程使用
1.一个进程内的两个线程
2.两个进程的两个线程
1.生产者消费者管程法
2。生产者消费者信号灯法
注意,2个以上线程唤醒存在虚假唤醒,if改为while循环判断
实现方式1.管程法
信号灯法(flag)
将数据缓存在线程内的线程本地存储机制,该线程可在生命周期内任意方法中缓存或获取缓存的数据
底层使用ThreadLocalMap实现,是Thread的局部变量,数据结构ThreadLocalMap
如果在线程池中使用ThreadLocal会造成内存泄漏,因为线程执行任务后不会被回收而是执行下一个任务,缓存的变量一直存在,用完后需要手动move()掉
应用场景:链接管理,一个线程持有一个链接,该链接对象可以在不同方法间传递
package java.util.concurrent;//juc,并发包
package java.util.concurrent.atomic;//原子性
package java.util.concurrent.locks;//Lock锁
package java.util.function;//函数式
降低耦合性,多线程尽量不用以前的implements Runnable实现,oop思想,抽象成一个资源类来操作
实现类有3个
常用实现类ReentrantLock.
和Condition组合使用
public class ReentrantLock implements Lock, java.io.Serializable//可重用锁,常用
//Constructors
public ReentrantLock() {
sync = new NonfairSync();//非公平锁
}
//Constructors
public ReentrantLock(boolean fair) {//FairSync公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
/**
公平锁:先来后到 非公平锁:可以插队(默认),synchronized是非公平锁
/**
private final ReentrantReadWriteLock.ReadLock readerLock;//读锁
private final ReentrantReadWriteLock.WriteLock writerLock;//写锁
读写锁
独占锁:一次只能被一个线程占有 共享锁:一次可以被多个线程占有
ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。只要没有写者, read lock可能被多个读线程同时持有。 write lock是独占的,即:写是一个线程写,读可以多个线程读
实现类
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
/**
* demo
*/
import java.util.HashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache cache = new MyCache();
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 写
*/
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
try {
readWriteLock.writeLock().lock();//并发写入,原子性操作,需要加锁
System.out.println("开始写入缓存:"+temp+"...");
cache.set(temp+"",Integer.valueOf(temp));
}finally {
readWriteLock.writeLock().unlock();
}
},Thread.currentThread().getName()).start();
}
/**
* 读
*/
for (int i = 0; i < 6; i++) {
final int temp = i;
new Thread(()->{
System.out.println("从缓存中取出数据"+cache.get(temp+""));
}).start();
}
}
}
class MyCache{
private volatile HashMap<String, Object> cache = new HashMap<String, Object>();
public void set(String key,Object Value){
cache.put(key,Value);
System.out.println(key+"已放入缓存...");
}
public Object get(String key){
return key == null ? null : cache.get(key);
}
}
开始写入缓存:1...
1已放入缓存... // 写是原子性操作
开始写入缓存:0...
0已放入缓存...
开始写入缓存:2...
2已放入缓存...
开始写入缓存:3...
3已放入缓存...
开始写入缓存:4...
4已放入缓存...
从缓存中取出数据0
从缓存中取出数据1
从缓存中取出数据2
从缓存中取出数据4
从缓存中取出数据null
从缓存中取出数据3
synchronized和lock对比
1.synchronized是内置关键字,Lock是一个接口
2.synchronized无法获取锁状态,Lock可以判断是否获得锁
3.synchronized隐式锁(出了作用域自动释放),lock显示锁(手动加锁解锁),不释放会产生死锁
4.synchronized如果有两个线程,并且线程B等待线程A,如果A阻塞,B就一直等待,Lock有thyLock()不一定一直等待
5.synchronized 可重入锁,不可中断,非公平锁,Lock可重入锁,可以判断锁,可以指定公平或非公平锁
6.Lock只有代码块锁,synchorized还有方法锁
7.Lock锁,JVM话费较少的时间来调度线程,性能好,有扩展性(提供更多子类)
8.可重入锁中,synchronized 只有一把锁,Lock有两把
锁中锁,也叫递归锁
/**
* 可重入锁:锁中锁.synchronized的demo
* A.lock会自动带入到B中,只有一把锁,B会阻塞
*/
public class RTTLockDemo {
public static void main(String[] args) {
Person person = new Person();
new Thread(()->{
person.eat();
},"A").start();
new Thread(()->{
person.eat();
},"B").start();
}
}
class Person{
public synchronized void eat(){
System.out.println(Thread.currentThread().getName()+".eat...");
drink();
}
public synchronized void drink(){
System.out.println(Thread.currentThread().getName()+".drink...");
}
}
A.eat...
A.drink...
B.eat...
B.drink...
/**
* 可重入锁:锁中锁.Lock的demo
* A.lock锁有两对,B会阻塞
*/
public class RTTLockDemo {
public static void main(String[] args) {
Person1 person = new Person1();
new Thread(()->{
person.eat();
},"A").start();
new Thread(()->{
person.eat();
},"B").start();
}
}
class Person1{
Lock lock = new ReentrantLock();
public void eat(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+".eat...");
//eat.lock中还有一把drink.lock,
drink();
}finally {
lock.unlock();
}
}
public void drink(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+".drink...");
}finally {
lock.unlock();
}
}
}
A.eat...
A.drink...
B.eat...
B.drink...
class MyLock{
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
System.out.println(Thread.currentThread().getName()+"线程加锁成功...");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
System.out.println(Thread.currentThread().getName()+"线程解锁成功...");
},"A").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
spinLockDemo.myLock();
System.out.println(Thread.currentThread().getName()+"线程加锁成功...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
System.out.println(Thread.currentThread().getName()+"线程解锁成功...");
},"B").start();
}
}
/**
* 自旋锁
*/
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
System.out.println(Thread.currentThread().getName()+"加锁中...");
//CAS自旋加锁
while (!atomicReference.compareAndSet(null,Thread.currentThread())){
}
}
//解锁
public void myUnLock(){
System.out.println(Thread.currentThread().getName()+"解锁中...");
//CAS解锁
atomicReference.compareAndSet(Thread.currentThread(),null);
}
}
A加锁中...
A线程加锁成功...
B加锁中...
A解锁中...
A线程解锁成功...
B线程加锁成功...
B解锁中...
B线程解锁成功...
两个线程相互持有对方需要的资源,形成僵持
package thread;
import java.util.concurrent.TimeUnit;
class TestDeadlockDemo{
public static void main(String[] args) {
String A = "A";
String B = "B";
new Thread(new DeadlockDemo(A,B),"a线程").start();
new Thread(new DeadlockDemo(B,A),"b线程").start();
}
}
public class DeadlockDemo implements Runnable {
private String A;
private String B;
public DeadlockDemo(String A,String B){
this.A = A;
this.B = B;
}
@Override
public void run() {
synchronized (A){
System.out.println(A+B);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B){
System.out.println(B+A);
}
}
}
}
排查死锁
jps -l查看进程号,jstack查看堆栈信息
C:\hot\devlopt\jdk\jdk8\bin目录下执行
PS C:\hot\devlopt\jdk\jdk8\bin> jps -l
13760 thread.TestDeadlockDemo
6608 org.jetbrains.jps.cmdline.Launcher
17436
5324 sun.tools.jps.Jps
PS C:\hot\devlopt\jdk\jdk8\bin> jstack 13760
........
Found one Java-level deadlock:
=============================
"b线程":
waiting to lock monitor 0x000001b8fafdc978 (object 0x00000000d6377d58, a java.lang.String),
which is held by "a线程"
"a线程":
waiting to lock monitor 0x000001b8fafd9f88 (object 0x00000000d6377d88, a java.lang.String),
which is held by "b线程"
Java stack information for the threads listed above:
===================================================
"b线程":
at thread.DeadlockDemo.run(DeadlockDemo.java:33)
- waiting to lock <0x00000000d6377d58> (a java.lang.String)
- locked <0x00000000d6377d88> (a java.lang.String)
at java.lang.Thread.run(Thread.java:750)
"a线程":
at thread.DeadlockDemo.run(DeadlockDemo.java:33)
- waiting to lock <0x00000000d6377d88> (a java.lang.String)
- locked <0x00000000d6377d58> (a java.lang.String)
at java.lang.Thread.run(Thread.java:750)
Found 1 deadlock.
方法上加synchronized, 锁的对象是方法的调用者,谁先拿到谁执行
锁的东西就两个,一个是方法的调用者:this.一个是Class
static,静态的,类一加载就执行,锁的是类模板,全局唯一,只有一个类对象,对象.class。static锁,不管几个对象调用,锁的是该唯一对象
是不是可以使用到流程里?
传统的通信 syncroized关键字和Object的wait(), notify/nofifyAll(),多个线程就会出现问题
juc通信 Lock接口和Condition接口的await(), signal()/signalAll()
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
condition.await();
condition.signal();
优势:Condition可以做到精准通知,如Thread A,B,C… A执行完B执行,B执行完C执行…
package juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class JucCommunication {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 20; i++) {
data.test1();
}
},"线程A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
data.test2();
}
},"线程B").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
data.test3();
}
},"线程C").start();
}
}
/**
* 资源类
*/
class Data {
/**
* 执行方法标识
*/
private int flag = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
protected void test1(){
lock.lock();
//判断,执行,通知
try {
while (flag !=1){
condition1.await();
}
//执行
System.out.println(Thread.currentThread().getName()+"AAAAAA");
flag = 2;
condition2.signalAll();//精准通知
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
protected void test2() {
lock.lock();
try {
while (flag !=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"BBBBBB");
flag = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
protected void test3() {
lock.lock();
try {
while (flag !=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"CCCCC");
flag = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
add是扩容操作,并发下会fail fast机制 提前失败
解决1.Vector :add加了synchronized
解决2.Collections工具类转换:Collections.synchronizedList(arrayList)
解决3.juc并发包,用CopyOnWriteArrayList代替ArrayList:读写分离
public HashSet() {
map = new HashMap<>();//hashSet就是hashMap
}
public boolean add(E e) {
return map.put(e, PRESENT)==null; //map的key是唯一的,所以set是不重复的
}
解决1.Collection工具类转换:Collections.synchronizedSet(HashSet)
解决2.juc并发包,用CopyOnWriteArraySet
解决1.juc并发包,用ConcurrentHashMap
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
类似于管道,先进先出。FIFO
如果队列满了,写就阻塞,需要等前面的元素拿出去后在可写入
反之,如果队列是空,读就阻塞,等待写入(生产)
队列的方法只有两种,添加元素和删除元素
4组API
1.抛出异常 2.不抛出异常 3.阻塞等待 4.超时等待
操作 | 抛出异常 | 不抛异常返回布尔 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add(E e) | offer(E e) | put(E e) | offer(E e, long timeout, TimeUnit unit) |
删除 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
获取队首元素 | element() | peek() | - | - |
import java.util.concurrent.ArrayBlockingQueue;
public class BlockingQueueDemo {
public static void main(String[] args) {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
for (int i = 0; i < 3; i++) {//添加超过队列长度抛出异常
arrayBlockingQueue.add(i);
System.out.println("添加元素"+i);
}
for (int i = 0; i < 4; i++) {//取出长度超过队列长度报错
System.out.println(arrayBlockingQueue.remove());
}
}
}
添加元素0
添加元素1
添加元素2
0
1
2
Exception in thread "main" java.util.NoSuchElementException
BlockingQueue
多线程和线程池中用的多
AbstractQueue
Deque
SynchronousQueue:只能存一个元素,只有take()空了才能put()进去,会阻塞
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue synchronousQueue = new SynchronousQueue<>();
new Thread(()->{
for (int i = 0; i < 3; i++) {
try {
System.out.println("put:"+i);
synchronousQueue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 3; i++) {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
put:0
0
put:1
1
put:2
2
池化技术:优化资源使用
1.降低资源消耗 2.提高相应速度 3.方便管理(线程复用,控制最大并发数,管理线程)
线程频繁的创建与销毁对资源的开销很大,引人线程池,重复利用
作用:提高响应速度,降低资源消耗,便于管理线程(核心线程数corePoolSize(),最大线程数maxmumPoolSize(),超时销毁机制keepAliveTime())
ExecutorService线程池接口
Executor工具类线程池的工厂类
Callable和Runnable都可以使用这个ExecutorService线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();//一个线程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//5个线程
ExecutorService threadPool2 = Executors.newCachedThreadPool();//线程数可伸缩
try {
for (int i = 0; i < 100; i++) {
threadPool2.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool1.shutdown();
}
}
}
pool-3-thread-1
pool-3-thread-1
pool-3-thread-2
pool-3-thread-3
pool-3-thread-4
pool-3-thread-5
pool-3-thread-2
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
/**newSingleThreadExecutor默认的拒绝策略
*private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
*/
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约21亿,容易导致oom移除
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质走的是这个方法创建线程池
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小 平时开启线程的个数
int maximumPoolSize,//最大线程数 阻塞队列满了会触发线程最大数
long keepAliveTime,//超时回收 核心线程外的其他线程超时没有任务回收
TimeUnit unit,//超时时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,不变
RejectedExecutionHandler handler) {//拒绝策略 >最大线程数+阻塞队列数触发
不用上面的Executor创建线程用原生ThreadPoolExecutor
import java.util.concurrent.*;
public class ExecutorServiceDemo {
public static void main(String[] args) {
/**
* @Range(from = 0, to = Integer.MAX_VALUE) int corePoolSize,
* @Range(from = 1, to = Integer.MAX_VALUE) int maximumPoolSize,
* @Range(from = 0, to = Long.MAX_VALUE) long keepAliveTime,
* @NotNull TimeUnit unit,
* @NotNull BlockingQueue workQueue,
* @NotNull ThreadFactory threadFactory,
* @NotNull RejectedExecutionHandler handler
*/
ExecutorService threadPool = new ThreadPoolExecutor(
2,
/**
*任务分cpu密集型,IO密集型
*cpu密集型:计算,逻辑等任务较多,IO型操作较少。线程数=cpu个数/cpu+1(超线程)
*IO密集型:网络的请求,文件的操作,数据库的交互。线程数=(1+线程等待的时间/线程cpu计算时间)*cpu个数*cpu使用率
*一般线程数=cpu个数*2
*/
Runtime.getRuntime().availableProcessors(),//获取cpu核数
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
// new ThreadPoolExecutor.AbortPolicy() 直接拒绝,抛出异常
// new ThreadPoolExecutor.CallerRunsPolicy()//哪个线程送过来返回给哪个线程执行,>maximumPoolSize+workQueue
// new ThreadPoolExecutor.DiscardPolicy()拒绝,不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy()//>maximumPoolSize+workQueue,尝试和最早的竞争,竞争不上丢弃,不抛异常
);
try {
for (int i = 0; i < 100; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
用ThreadPoolExecutor创建
减法计数器
//Constructs
public CountDownLatch(int count) {//要等待线程的个数
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* 使当前线程进入同步阻塞队列进行等待,直到计数器的值减到0或者当前
* 线程被中断,当前线程就会被唤醒
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
* 带有超时时间的
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* 计数器-1,减到0则会唤醒所有等待在这个CountDownLatch上的线程
*/
public void countDown() {
sync.releaseShared(1);
}
/**
* 获得计数器的值
*/
public long getCount() {
return sync.getCount();
}
//demo
public static void main(String[] args) throws InterruptedException {
// CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
final int temp = i;//lambda里的i好像是final的,lambda也被称为闭包???
new Thread(()->{
//lambda无法访问外部变量,通过fianl型的中间变量获取,所以不能直接拿到i
//直接拿i报错,java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
System.out.println("第"+ temp +"个线程"+Thread.currentThread().getName()+"is running");
doneSignal.countDown();//执行一个线程计数器减1
}).start();
}
doneSignal.await();//阻塞当前main线程,计数器的值减到0会被唤醒
System.out.println("main Thread is running...");
}
作用和场景
1.实现最大并发性
2.开始执行前等待其他线程各自执行完后进行汇总合并,如使用多线程统计数据后做一个汇总
确定,一次性工具类,计数器的值只能在构造函数中初始化一次,之后不能再次赋值,使用完后不能再次使用
加法计数器
/**
*重置计数器,可重复使用
*/
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
/**
*获得阻塞线程数量
*/
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
/**
*阻塞线程是否被中断
*/
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
/**
*demo
*/
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("开始汇总...");});
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(()->{
try {
System.out.println(temp+"等待");
cyclicBarrier.await();//阻塞当前线程
System.out.println(temp+"开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
1等待
2等待
3等待
4等待
5等待
6等待
7等待
8等待
9等待
10等待
开始汇总...
10开始执行
1开始执行
2开始执行
3开始执行
5开始执行
6开始执行
4开始执行
9开始执行
8开始执行
7开始执行
案例:模拟人都到齐,一声令下,所有人都干活
使用场景:多线程计算数据,最后合并计算结果。
CyclicBarrier和CountDownLatch的区别
CountDownLatch:阻塞当前线程,待其他线程完成后,自己在执行
CyclicBarrier:多个线程一起阻塞,达到屏障后所有线程一起执行
计数许可信号量,多个共享资源的互斥使用,并发限流,控制最大并发线程数。可以用来做限流,控制同时访问资源的线程或用户数量
/**
*Constructs
*permits:许可数量。即同时允许多少线程访问
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);//默认非公平
}
/**
*Constructs
*permits:许可数量。即同时允许多少线程访问
*fair:是否是公平的,
*FairSync(permits):按照请求时间获得许可,即,先发送请求的先活得许可
*NonfairSync(permits):先发送请求的未必先获得许可,有助于提高系统的吞吐量,但是可能导致某些线程始终获取不到许可
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
/**
*Method
*获取一个许可
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
*归还一个许可,有才可以还
*/
public void release() {
sync.releaseShared(1);
}
/**
*获取多个许可
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
/**
*归还多个许可,归还<=已获得
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
/**
*上面4个方法都是会阻塞的,获取许可非阻塞的方法tryAcquire()/tryAcquire(int permits)/
*tryAcquire(long timeout, TimeUnit unit)/tryAcquire(int permits, long timeout, TimeUnit unit)
*/
/**
*获取剩余的许可信号量个数
*/
public int availablePermits() {
return sync.getPermits();
}
/**
*demo
*/
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
private static final int MAX_THREAD_NUM = 6;
private static final int NOW_PERSON_NUM = 10;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(MAX_THREAD_NUM);
for (int i = 0; i < NOW_PERSON_NUM; i++) {
final int temp = i;
new Thread(()->{
try {
semaphore.acquire();//得到信号量
System.out.println(temp+"抢到票");
TimeUnit.SECONDS.sleep(2);
System.out.println(temp+"离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放信号量
}
}).start();
}
}
0抢到票
1抢到票
2抢到票
3抢到票
4抢到票
5抢到票 //6个位置都没了
0离开
2离开
1离开 //腾出3个位置
6抢到票
7抢到票 //剩余1个位置
3离开 //剩余2个位置
8抢到票 //剩余1个位置
9抢到票 //剩余0个位置
4离开
5离开
6离开
8离开
9离开
7离开 //都有票了,腾出6个位置
jdk1.8以后简化编程模型,4大函数式接口,新版本框架底层大量应用
函数式接口都可简化成lambda表达式
1.Function 函数式接口 ,一个参数,一个返回值
/**
* Source
*/
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {//传入参数T类型,返回参数R类型
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
/**
* demo
*/
import java.util.function.Function;
public class FunctionDemo {
public static void main(String[] args) {
Function<Integer,String> function = new Function<Integer,String>() {
@Override
public String apply(Integer s) {
return s.toString();
}
};
/**
* 所有的函数式接口都可简化为Lambda表达式
*/
Function<Integer,String> function2 = (num)->{//num的括号可以省略
return num.toString();
};
for (int i = 0; i < 3; i++) {
System.out.println(function2.apply(999));
}
}
}
999
999
999
2.Predicate:断定型接口,有一个参数,返回boolean
/**
* Source
*/
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
/**
* demo
*/
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
Predicate<String> predicate1 = str->{return str.isEmpty();};
System.out.println(predicate.test(""));
System.out.println(predicate1.test("asd"));
}
}
true
false
3.Supplier:供给型接口:没有参数,只有返回值
/**
* Source
*/
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
/**
* demo
*/
@Test
public void testSupplier(){
Supplier supplier = new Supplier() {
@Override
public String get() {
return "hello";
}
};
Supplier supplier1 = ()->{return "rock";};
System.out.println(supplier.get());
System.out.println(supplier1.get());
}
hello
rock
4.Consumer:消费型接口 只有输入值,没有返回值
/**
* Source
*/
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
/**
* demo
*/
@Test
public void testConsumer(){
Consumer consumer = new Consumer() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer.accept("123");
Consumer consumer1 = str->{System.out.println(str);};
consumer1.accept("rock");
}
123
rock
集合存储,流计算
/**
*source
*Collection类型转换成Stream类型
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//Stream接口的过滤方法,参数是断定型接口类型
Stream<T> filter(Predicate<? super T> predicate);
/**
*demo
*/
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* 1.id为偶数的用户
* 2.年龄必须大于23岁
* 3.用户名字母倒着排序
* 4.用户名转为大写
* 5.只输出一个用户
*/
public class StreamTest {
List<Person> list = new ArrayList<Person>();
@Test
public void test(){
for (int i = 0; i < 10; i++) {
list.add(new Person(i, "hot"+i,i*i));
}
System.out.println(list.get(list.size()-1).getAge());
/**
*List转换成Stream流
*链式编程
*/
list.stream().filter((u)->{return u.getId()%2==0;})//id为偶数的用户
.filter((u)->{return u.getAge()>23;})//年龄必须大于23岁
.sorted((u1,u2)->{return u2.getName().compareTo(u1.getName());})//用户名字母倒着排序
.map((u)->{ u.getName().toUpperCase();//用户名转为大写
return u;})
.limit(1)//只输出一个用户
.forEach(System.out::println);
}
jdk1.7的,计算大数据量使用,几十亿。一个大任务分成几个子任务处理,结果汇总,会工作窃取,A任务先执行完后会窃取B任务执行,任务是双端队列,从尾部窃取,也会存在竞争同一个任务,但优大于劣,适合于cpu密集型任务处理 fork/join和map/reduce的意思差不多
public interface ExecutorService extends Executor {
public abstract class AbstractExecutorService implements ExecutorService {
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
//Runnable,Callable都返回的是FutureTask
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
步骤:1.大任务分割为多个子任务 2.通过Callable获取返回值提交给线程池处理 3.FutureTask接受任务 4.在主线程通过FutureTask.get()阻塞式的获取结果 5.汇总
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp = 10000L;
public ForkJoinDemo(Long start,Long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if(end-start<temp){
Long sum = 0L;
for (long i = start; i <=end ; i++) {
sum+=i;
}
return sum;
}else {
long middle = (start+end)/2;
ForkJoinDemo task1 = new ForkJoinDemo(start,middle);
task1.fork();//任务1压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
task2.fork();
return task1.join()+task2.join();
}
}
}
import org.junit.Test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class TestForkJoinDemo {
private Long num = 10_0000_0000L;
/**常规计算
* 500000000500000000
* 6730
*/
@Test
public void test1(){
Long sum = 0L;
Long startTime = System.currentTimeMillis();
for (Long i = 1L; i <= num; i++) {
sum+=i;
}
Long endTime = System.currentTimeMillis();
System.out.println(sum);
System.out.println(endTime-startTime);
}
/**ForkJoin计算
* 500000000500000000
* 54378
*/
@Test
public void test2() throws ExecutionException, InterruptedException {
Long startTime = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> forkJoinTask = new ForkJoinDemo(0L,10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTask);//提交任务
Long sum = submit.get();
Long endTime = System.currentTimeMillis();
System.out.println(sum);
System.out.println(endTime-startTime);
}
/**并行流计算
* 359
* 500000000500000000
*/
@Test
public void test3() {
Long startTime = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
Long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
System.out.println(sum);
}
}
多线程异步回调
Java内存模型,不存在的,只是概念,规范,有4对,8种操作,必须成对使用,和{},“”,()一个道理
根据上图为了保证线程直接的可见性,便引人了Volatile
import java.util.concurrent.TimeUnit;
public class VolatileTest {
/**
* volatile保证主内存中的数据对其他线程可见性
*/
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num == 0){
System.out.println(num);}
}).start();
TimeUnit.SECONDS.sleep(2);
num = 1;
System.out.println(num);
}
}
0
0
0
1
public class VolatileTest {
/**
* volatile不保证原子性
*/
//private volatile static int num = 0;
private volatile static AtomicInteger num = new AtomicInteger(0);//CAS,效率很高
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
/**
* main和gc。礼让,保证其他线程先执行完
*/
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
/** bytecode
0 getstatic #9 //获取值
3 iconst_1
4 iadd //+1
5 putstatic #9 //设置值
8 return
* num++是分几个步骤完成的,volatile保证不了原子性,结果必然<=10000
* synchroined和lock保证原子性但开销大
* 可以使用CAS原子类java.util.concurrent.atomic.AtomicInteger;//Atomicxxx
* CAS比锁高效很多倍
*/
private static void add(){
//num++;
num.incrementAndGet();
}
}
//9347
10000
指令重排:code–>编译器优化重排–>指令并行重排–>内存系统重排–>执行
int x = 1;//1
int y = 2;//2
x = x+5; //3
y = x*x; //4
期望执行顺序1234,但可能会编程2134或1324
不可能4123。指令重排会处理数据之间的依赖数据
int a,b,x,y=0;
ThreadA ThreadB
x=a//1 y=b//3
b=1//2 a=2//4
期望的执行顺序是1,2,3,4.x=a,y=b,b=1,a=2;
由于1和2没有依赖关系指令重排可能顺序是2,1,同理,4,3
b=1//2 a=2//4
x=a//1 y=b//3
得出诡异的结果a=2,y=1
写1000W次可能不会出现指令重排造成的问题,但逻辑上是存在会产生诡异结果的
volatile就是避免指令重排
实现原理
内存屏障
volatile内存屏障在单例中使用很多
/**
* 饿汉
* @author Rock
*/
public class Hungry {
/**
* 类一加载就占用浪费了很多内存资源,所以有了懒汉单例
*/
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
/**
* 单例构造器私有,外部无法new对象,保证对象唯一
*/
private Hungry(){
}
private static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName());
}
private static LazyMan lazyMan;
/**
* 单线程下没有问题,多线程情况下会有问题,所以就有了DCL懒汉式
* @return
*/
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
getInstance();
}).start();
}
}
}
Thread-0
Thread-1
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName());
}
//防止指令重排
private volatile static LazyMan lazyMan;
/**
* 双重检测锁模式,DCL懒汉式单例
* @return
*/
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
/**
* 1.分配内存空间
* 2.执行构造方法进行初始化对象
* 3.对象指向内存空间
* 指令重排顺序有可能是132,执行13此时如果其他线程进入就会发现lazyMan != null
* 构造方法没执行,lazyMan指向的空间是空的,所以必须加上volatile
* DCL懒汉式 = 双重检测所+volatile保证多线程下也是单例的
*/
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
getInstance();
}).start();
}
}
}
Thread-0
/**
* 反射破坏单例
* 1.通过无参构造器反射出新对象
*/
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
LazyMan instance = LazyMan.getInstance();//正常获取对象1
Constructor<LazyMan> declaredConstructors = LazyMan.class.getDeclaredConstructor();
declaredConstructors.setAccessible(true);//禁用访问安全检查,速度会提升
LazyMan instance1 =declaredConstructors.newInstance();//反射获取对象2
System.out.println(instance);
System.out.println(instance1);
}
single.LazyMan@7f31245a
single.LazyMan@6d6f6e28
/**
* 解决,构造器中类模板上锁
*/
private LazyMan(){
// System.out.println(Thread.currentThread().getName());
synchronized (LazyMan.class){
if(lazyMan!=null){
throw new RuntimeException("构造器已锁死");
}
}
}
/**
* 2中的instance也反射获取
*/
LazyMan instance =declaredConstructors.newInstance();
single.LazyMan@7f31245a
single.LazyMan@6d6f6e28
/**
* 解决:锁的同时判断标识
*/
private static Boolean refectFlag = false;
synchronized (LazyMan.class){
if(refectFlag == false){
refectFlag = true;
}else{
throw new RuntimeException("标识防止反射二次创建");
}
if(lazyMan!=null){
throw new RuntimeException("构造器已锁死");
}
}
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at single.LazyMan.main(LazyMan.java:67)
Caused by: java.lang.RuntimeException: 标识防止反射二次创建
at single.LazyMan.<init>(LazyMan.java:15)
... 5 more
/**
* 标识也可以破解
*/
Field flagField = LazyMan.class.getDeclaredField("refectFlag");//获取字段
//反射第一个对象instance后在设置字段值
flagField.set(instance,false);
/**
* 静态内部类单例
*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
/**
* 反射不能破坏枚举单例
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class TestEnum{
EnumSingle single = EnumSingle.INSTANCE;
}
compareAndSwap,compareAndSet更符合java的操作思想。比较并交换
比较当前工作内存中的值和主内存中的值,如果是期望的值,那么交换,如果不是就一直循环
atomicInteger.incrementAndGet();
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;//valueOffset:内存地址偏移值
}
/**
* Unsafe类是java调用c++操作内存的类库,Java不能直接操作内存,Java通过native调用C++操作内存
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset//获取内存偏移值
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);//获取内存中的值
/**
* CAS,期望,更新;
* var1(对象本身)var2(static时获取的内存地址偏移值)是var5(内存中的值),那么交换成var5 + var4(传进来的1)
* while自旋
* var5 + var4:直接操作的内存值,效率高
*/
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
自旋会耗时,但比java的效率高很多
一次性只能保证一个共享变量的原子性,但也够使用
ABA问题:CAS有ABA问题
a = 2;ThreadA操作了 a+1;a+4;a-3…a=2;。ThreadB获取a,a没有变,但是a经过A一系列的操作B不知情,所以此a非彼a
import java.util.concurrent.atomic.AtomicInteger;
public class ABADemo {
@Test
public void test(){
AtomicInteger a = new AtomicInteger(10);
//true
System.out.println(a.compareAndSet(10, 20));
//20
System.out.println(a.get());
//true
System.out.println(a.compareAndSet(20, 10));
//10
System.out.println(a.get());
//true
System.out.println(a.compareAndSet(10, 30));
//30
System.out.println(a.get());
}
}
解决:原子引用 AtomicStampedReference
带版本号的原子操作
其他知识点
Integer对象使用的缓存机制,默认范围是-128~127,只要超过这个范围就会new。推荐使用静态工厂方法valueOf()获取对象实例,而不是new,因为valueOf()从缓存IntegerCache.cache中取值,而new会创建新的对象分配新的内存空间,同理,其他包装类也看一下,泛型是包装类型的一定要注意引用问题
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
@Test
public void testInteger(){
Integer x = 127;
Integer a = 127;
Integer y = 128;
Integer c = 128;
System.out.println(x == a);//true
System.out.println(y == c);//false
System.out.println(x.equals(a));//true
System.out.println(y.equals(c));//true
}
Atomic类会在热点数据上损耗性能,用LongAddr