点我跳转至JUC并发编程总结(二)
基础部分内容
线程和进程
CPU密集型和IO密集型 获取cpu核心数System. out. println ( Runtime. getRuntime ( ) . availableProcessors ( ) ) ;
线程的几个状态,打开Thread类看源码public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
sleep和wait区别 TimeUnit.SECONDS.sleep(5);
来自不同类 thread/Object 锁释放 不释放/释放 适用范围 任何地方/同步代码块 捕获异常 需要/不需要
synchronized和Lock
1. 区别
synchronized是关键字,Lock是类 synchronized无法判断锁状态,Lock可以判断是否获取到了锁 synchronized会自动释放,Lock必须手动释放不然会死锁。 synchronized会阻塞会一直等待,Lock不一定一直等待 synchronized可重入锁非公平;Lock,可重入锁,可以判断锁,非公平(可以设置) synchronized适合少量代码同步问题,lock适合大量。
2. 生产者消费者问题
synchronized版,防止虚假唤醒问题。判断有if替换为while
class Data {
private int num = 0 ;
public synchronized void increment ( ) throws InterruptedException {
while ( num != 0 ) {
this . wait ( ) ;
}
num++ ;
System. out. println ( "生产了一个:" + num) ;
this . notifyAll ( ) ;
}
public synchronized void decrement ( ) throws InterruptedException {
while ( num == 0 ) {
this . wait ( ) ;
}
num-- ;
System. out. println ( "消费了一个:" + num) ;
this . notifyAll ( ) ;
}
public static void main ( String[ ] args) {
System. out. println ( Runtime. getRuntime ( ) . availableProcessors ( ) ) ;
Data data = new Data ( ) ;
new Thread ( ( ) - > {
data. increment ( ) ;
} ) . start ( ) ;
new Thread ( ( ) - > {
data. decrement ( ) ;
} ) . start ( ) ;
}
}
class Data2 {
private int num = 0 ;
Lock lock = new ReentrantLock ( ) ;
Condition condition= lock. newCondition ( ) ;
public void increment ( ) {
try {
lock. lock ( ) ;
while ( num != 0 ) {
condition. await ( ) ;
}
num++ ;
System. out. println ( "生产了一个:" + num) ;
condition. signalAll ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} finally {
lock. unlock ( ) ;
}
}
public void decrement ( ) {
lock. lock ( ) ;
try {
while ( num == 0 ) {
condition. await ( ) ;
}
num-- ;
System. out. println ( "消费了一个:" + num) ;
condition. signalAll ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} finally {
lock. unlock ( ) ;
}
}
}
3. 相关面试题
public class Demo {
public static void add ( List< Integer> a, Integer b) {
a. add ( b) ;
}
public static void main ( String[ ] args) throws InterruptedException {
List< Integer> a= new LinkedList < > ( ) ;
ReentrantLock lock= new ReentrantLock ( ) ;
Condition condition1 = lock. newCondition ( ) ;
Condition condition2 = lock. newCondition ( ) ;
AtomicInteger num= new AtomicInteger ( 1 ) ;
Thread t1= new Thread ( ( ) - > {
for ( int i= 0 ; i< 100 ; i++ ) {
lock. lock ( ) ;
try {
while ( num. get ( ) != 1 ) {
condition1. await ( ) ;
}
if ( i% 2 == 0 ) {
add ( a, i) ;
num. set ( 2 ) ;
}
condition2. signal ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} finally {
lock. unlock ( ) ;
}
}
} ) ;
Thread t2= new Thread ( ( ) - > {
for ( int i= 0 ; i< 100 ; i++ ) {
lock. lock ( ) ;
try {
while ( num. get ( ) != 2 ) {
condition2. await ( ) ;
}
if ( i% 2 != 0 ) {
add ( a, i) ;
num. set ( 1 ) ;
}
condition1. signal ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} finally {
lock. unlock ( ) ;
}
}
} ) ;
t1. start ( ) ;
t2. start ( ) ;
t1. join ( ) ;
t2. join ( ) ;
System. out. println ( a) ;
}
}
4. 小结
condition,精准唤醒。如上线程加减问题。 8锁现象,锁的是谁?
集合类线程不安全
普通集合类用多线程操作会出现并发修改异常。ConcurrentModificationException
解决方案
List< String> list = new Vector < > ( ) ;
List< String> list2 = Collections. synchronizedList ( new ArrayList < > ( ) ) ;
List< String> list3 = new CopyOnWriteArrayList < > ( ) ;
Set< String> set = Collections. synchronizedSet ( new HashSet < > ( ) ) ;
Set< String> set2 = new CopyOnWriteArraySet < > ( ) ;
ConcurrentHashMap< String, String> map= new ConcurrentHashMap < > ( ) ;
线程池和辅助类相关内容
线程类接口
callable
示例:public class Demo {
public static void main ( String[ ] args) throws ExecutionException, InterruptedException {
MyThread thread = new MyThread ( ) ;
FutureTask ft= new FutureTask ( thread) ;
new Thread ( ft, "A00" ) . start ( ) ;
String result= ( String) ft. get ( ) ;
FutureTask ft2= new FutureTask ( new MyThread ( ) ) ;
new Thread ( ft2, "A01" ) . start ( ) ;
String result2= ( String) ft. get ( ) ;
}
}
class MyThread implements Callable < String> {
@Override
public String call ( ) throws Exception {
return "返回泛型接口的String类" ;
}
}
常用辅助类及读写锁、队列
1. CountDownLatch
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。 示例:
public class Demo03 {
public static void main ( String[ ] args) throws InterruptedException {
CountDownLatch cdl= new CountDownLatch ( 5 ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
new Thread ( ( ) - > {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "线程执行完成" ) ;
cdl. countDown ( ) ;
} ) . start ( ) ;
}
cdl. await ( ) ;
System. out. println ( "exit" ) ;
}
}
2. CyclicBarrier
大概的意思就是一个可循环利用的屏障。它的作用就是会让所有线程都等待完成后才会继续下一步行动。 示例:
CyclicBarrier cb= new CyclicBarrier ( 5 , ( ) - > {
System. out. println ( "五个县城全部执行完成!" ) ;
} ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
new Thread ( ( ) - > {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "线程执行完成" ) ;
cb. await ( ) ;
} ) . start ( ) ;
}
3. Semaphore
Semaphore 是一个计数信号量,必须由获取它的线程释放。常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。Semaphore 只有3个操作:初始化、增加、减少。 示例:
public class StudySemaphore {
public static void main ( String[ ] args) {
ExecutorService executorService = Executors. newCachedThreadPool ( ) ;
Semaphore semaphore = new Semaphore ( 3 ) ;
for ( int i= 0 ; i< 10 ; i++ ) {
final long num = i;
executorService. submit ( new Runnable ( ) {
@Override
public void run ( ) {
try {
semaphore. acquire ( ) ;
System. out. println ( "Accessing: " + num) ;
Thread. sleep ( new Random ( ) . nextInt ( 5000 ) ) ;
semaphore. release ( ) ;
System. out. println ( "Release..." + num) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
} ) ;
}
executorService. shutdown ( ) ;
}
}
4. 读写锁
ReadWriteLock
同Lock
一样也是一个接口,提供了readLock
和writeLock
两种锁的操作机制,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的(排他的)。每次只能有一个写线程,但是可以有多个线程并发地读数据。 所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。 互斥原则:
读-读能共存 (但不能写) 读-写不能共存 (脏读、不可重复读、幻读) 写-写不能共存 (显然不能…)
5. 队列
队列关系模型。 对列四组api:1.抛异常。2.不抛异常。3.阻塞等待。4.超时等待 用同步队列实现生产者消费者模型。
SynchronousQueue< String> blockingDeque= new SynchronousQueue < String> ( ) ;
new Thread ( ( ) - > {
blockingDeque. put ( "1" ) ;
blockingDeque. put ( "2" ) ;
blockingDeque. put ( "3" ) ;
} , "生产者" ) . start ( ) ;
new Thread ( ( ) - > {
System. out. println ( "消费了:" + blockingDeque. take ( ) ) ;
System. out. println ( "消费了:" + blockingDeque. take ( ) ) ;
System. out. println ( "消费了:" + blockingDeque. take ( ) ) ;
} , "消费者" ) . start ( ) ;
线程池
工具类,三大方法。
ExecutorService tp1= Executors. newSingleThreadExecutor ( ) ;
ExecutorService tp2= Executors. newFixedThreadPool ( 5 ) ;
ExecutorService tp3= Executors. newCachedThreadPool ( ) ;
for ( int i = 0 ; i < 50 ; i++ ) {
tp1. execute ( ( ) - > {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "ok" ) ;
} ) ;
}
七大参数
我们打开ThreadPoolExecutor
构造方法,可以看到这七大参数。 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;
}
关于拒绝策略,提供了以下几个。new ThreadPoolExecutor. AbortPolicy ( ) ;
new ThreadPoolExecutor. CallerRunsPolicy ( ) ;
new ThreadPoolExecutor. DiscardOldestPolicy ( ) ;
new ThreadPoolExecutor. DiscardPolicy ( ) ;
示例
ExecutorService pool= new ThreadPoolExecutor ( 3 ,
5 ,
3 , TimeUnit. SECONDS,
new LinkedBlockingDeque < > ( ) ,
Executors. defaultThreadFactory ( ) ,
new ThreadPoolExecutor. CallerRunsPolicy ( ) ) ;
for ( int i = 0 ; i < 50 ; i++ ) {
int finalI = i;
pool. execute ( new Thread ( ( ) - > {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "------" + finalI) ;
try {
TimeUnit. SECONDS. sleep ( 5 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} ) ) ;
}
线程设置
cpu密集型:几核就是几,保证占用率最高。 io密集型:应该大于你程序中十分消耗io的线程。例如下载任务。
Reference:
https://www.jianshu.com/p/333fd8faa56e https://www.jianshu.com/p/e233bb37d2e6 https://www.jianshu.com/p/ec637f835e08 https://blog.csdn.net/j080624/article/details/82790372 https://blog.csdn.net/youanyyou/article/details/78990156