java提高并发方向_java并发学习

Java Concurrent Learn

对java并发的一些学习,之前总结在幕布上,为了加深印象,这里重新学习下。

并发级别

阻塞 - 对临界区的代码串行化执行

synchronized

ReentrantLock

无饥饿 - 在优先级并发中,可能低优先级的永远不能执行的情况,需要避免就叫无饥饿

队列排队请求资源

公平锁

无障碍 - 读写锁控制力度不同,这里主要是乐观读

乐观锁-一致性标记,状态版本控制

无锁 - 比较交换,不是真正的不加锁,而是用java本地Unsaft 的cas方法

CAS - compare and swap

无等待 - 读写不等待,在写的时候,重新复制一个对象对其操作,操作完之后在合适的时机赋值回去,这种只适合极少量写,大量读的情况

RCU - read copy update

并发原则 - 三大原则

原子性 - atomic*原子操作类

可见性 - volatile关键字保证

有序性 - happen-before规则

线程创建

实现Runnable接口- 其实所有创建方式都是需要实现Runnable接口

public class RunnableTest implements Runnable {

@Override

public void run() {}

}

继续Thread

public class ThreadTest extends Thread {

@Override

public void run() {}

}

用Callable接口实现类构造FutureTask

public class CallableTest implements Callable {

@Override

public Object call() throws Exception {

return null;

}

}

FutureTask futureTask = new FutureTask(new CallableTest());

同步控制

synchronized Object.wait() Object.notify

synchronized同步关键字,对类,方法,对象进行修饰,修饰后获取对应监视器才能进入修饰的临界区

Object.wait(),Object.notify(),Object.notifyAll()方法必须在获取到监视器对象后才能调用,即要写在synchronized内部

Object.wait()之后会将当前线程放入对象的wait set中,释放获取的监视器对象

Object.notify()会从wait set随机对一线程唤醒,Object.notifyAll()会从wait set所有线程唤醒,不会立即释放获取的监视器,待结束临界区才释放,唤醒不代表被唤醒线程继续执行,其还需要争抢获取监视器才能继续执行

/**

* @author tomsun28

* @date 19:49 2020-02-19

*/

public class Demo {

private static final Logger LOGGER = LoggerFactory.getLogger(Demo.class);

private final static Demo demo = new Demo();

public static void waitDemo() {

synchronized (demo) {

try {

// 调用wait方法必须获取对应的监视器,在wait之后,释放监视器

LOGGER.info("获取demo对象监视器1");

demo.wait();

LOGGER.info("释放demo对象监视器1");

Thread.sleep(2000);

LOGGER.info("释放demo对象监视器2");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void notifyDemo() {

// 从等待队列中唤醒一个

synchronized (demo) {

LOGGER.info("获取demo对象监视器2");

demo.notify();

LOGGER.info("释放demo对象监视器3");

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

LOGGER.info("释放demo对象监视器4");

}

}

public static void main(String[] args) {

new Thread(() -> waitDemo()).start();

new Thread(() -> notifyDemo()).start();

}

}

ReentrantLock Condition

重入锁(可重复获取的锁),重入锁内部的Condition

ReentrantLock lock几次,就需要unlock几次,lock之后需要紧跟tay finally代码,finally第一行就要unlock

Condition功能用法和Object.wait notify差不多,只不过其搭配重入锁使用,不用在使用前获取对象监视器

Condition.await()会释放当前拥有的锁,并挂起当前线程

Conditon.singal()会随机唤醒当前await的线程,Condition.singalAll()会唤醒所有,当然同Object.notify()唤醒不代表被唤醒线程可以继续执行,其还需要争抢获取到锁后继续执行

/**

* @author tomsun28

* @date 22:15 2019-10-27

*/

public class ReenterLockCondition {

public static void main(String[] args) throws InterruptedException{

ReentrantLock reentrantLock = new ReentrantLock();

Condition condition = reentrantLock.newCondition();

new Thread(() -> {

// 先获取重入锁

System.out.println("0 - 获取锁");

reentrantLock.lock();

try {

System.out.println("0 - 释放锁,挂起线程");

// 同 Object.wait相似 其和ReentrantLock绑定,这里就是释放锁,挂起线程

condition.await();

System.out.println("0 - 等待锁释放,thread is fin");

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

reentrantLock.unlock();

}

}).start();

System.out.println("1 - 睡4秒");

Thread.sleep(4000);

System.out.println("1 - 等待获取锁");

reentrantLock.lock();

try {

System.out.println("1 - condition 唤醒线程");

condition.signal();

Thread.sleep(4000);

System.out.println("1 - 睡4秒释放锁");

} finally {

reentrantLock.unlock();

}

}

}

Semaphore

信号量-不同于锁,其可以控制进入临界区的线程数量,锁只能是一个线程进入临界区

构造函数可以初始化信号数量,semaphore.acquire()消耗一个信号,semaphore.release()释放一个信号,当所剩的信号为0时则不允许进入临界区

/**

* @author tomsun28

* @date 22:53 2019-10-27

*/

public class SemaphoreDemo {

public static void main(String[] args) {

final Semaphore semaphore = new Semaphore(5);

final AtomicInteger atomicInteger = new AtomicInteger(1);

Runnable thread1 = () -> {

try {

semaphore.acquire();

Thread.sleep(2000);

System.out.println(atomicInteger.getAndAdd(1));

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

semaphore.release();

}

};

ExecutorService executorService = Executors.newFixedThreadPool(20);

IntStream.range(0,20).forEach(i -> executorService.execute(thread1));

executorService.shutdown();

}

}

ReetrantReadWriteLock

读写锁,之前的重入锁还是synchronized,对于读写都是同样的加锁

ReadWriteLock把读和写的加锁分离,读读不互斥,读写互斥,写写互斥提高了锁性能,

需要注意,当有读锁存在时,写锁不能被获取,在大量一直在进行读锁的时候,会造成写锁的饥饿,即写锁一直获取不到锁

/**

* @author tomsun28

* @date 21:35 2020-02-19

*/

public class ReadWriteLockDemo {

public static void main(String[] args) {

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

new Thread(() -> {

readWriteLock.readLock().lock();

readWriteLock.readLock().lock();

try {

System.out.println("获取2读锁");

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

readWriteLock.readLock().unlock();

readWriteLock.readLock().unlock();

System.out.println("释放2读锁");

}

}).start();

new Thread(() -> {

readWriteLock.readLock().lock();

try {

System.out.println("获取3读锁");

Thread.sleep(6000);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

readWriteLock.readLock().unlock();

System.out.println("释放3读锁");

}

}).start();

new Thread(() -> {

readWriteLock.writeLock().lock();

try {

System.out.println("获取1写锁");

} finally {

readWriteLock.writeLock().unlock();

System.out.println("释放1写锁");

}

}).start();

}

}

StampedLock

上面的读写锁有一个缺点,即可能写饥饿,StampedLock解决这个问题

使用乐观读 - 使用标记戳来判断这次读的过程是否有写的过程,若有则重新读取或转化为悲观读

/**

* @author tomsun28

* @date 21:57 2020-02-19

*/

public class StampedLockDemo {

public static void main(String[] args) {

StampedLock stampedLock = new StampedLock();

AtomicInteger flag = new AtomicInteger(0);

new Thread(() -> {

long writeStamped = stampedLock.writeLock();

try {

System.out.println("获取写锁,休息1.006秒");

Thread.sleep(1006);

System.out.println("当前值为: " + flag.getAndAdd(1));

} catch (InterruptedException e) {

e.printStackTrace();

} finally{

stampedLock.unlockWrite(writeStamped);

System.out.println("释放写锁");

}

}).start();

new Thread(() -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

long stamp = stampedLock.tryOptimisticRead();

int readValue = flag.intValue();

while (!stampedLock.validate(stamp)) {

System.out.println("重试乐观读");

stamp = stampedLock.tryOptimisticRead();

readValue = flag.intValue();

}

System.out.println("乐观读为:" + readValue);

}).start();

}

}

CountDownLatch

并发减数拦截器,await()拦截阻塞当前线程,当减数器为0时通行

/**

* @author tomsun28

* @date 23:35 2019-10-27

*/

public class CountDownLatchDemo {

public static void main(String[] args) throws InterruptedException {

CountDownLatch countDownLatch = new CountDownLatch(10);

AtomicInteger atomicInteger = new AtomicInteger(1);

Thread threadd = new Thread(() -> {

try {

Thread.sleep(1000);

System.out.println(" thred check complete" + atomicInteger.getAndIncrement());

countDownLatch.countDown();

} catch (InterruptedException e) {

e.printStackTrace();

}

});

ExecutorService executorService = Executors.newFixedThreadPool(2);

IntStream.range(0, 10).forEach(i -> executorService.submit(threadd));

countDownLatch.await();

System.out.println("fin");

}

}

CyclicBarrier

栅栏,功能类似于ConutDownLatch,不过可以循环重置处理

ConutDownLatch是减数器为0时触发,并重置减数器循环等待下一次触发,并且可以提供触发方法

/**

* @author tomsun28

* @date 23:59 2019-10-27

*/

public class CyclicBarrierDemo {

public static void main(String[] args) {

CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {

System.out.println("this cyclic ok");

});

AtomicInteger atomicInteger = new AtomicInteger(1);

ExecutorService executorService = Executors.newFixedThreadPool(10);

IntStream.range(0, 100).forEach(i -> {

executorService.execute(() -> {

try {

Thread.sleep(1000);

System.out.println(atomicInteger.getAndIncrement());

cyclicBarrier.await();

} catch (BrokenBarrierException | InterruptedException e) {

e.printStackTrace();

}

});

});

executorService.shutdown();

}

}

LockSupport

同步工具类,比起wait notify要方便很多

LockSupport.park()是在当前线程阻塞挂起

LockSupport.unpark(thread)唤醒指定线程

/**

* @author tomsun28

* @date 00:39 2019-10-28

*/

public class LockSupportDemo {

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(() -> {

try {

LockSupport.park();

LockSupport.park();

System.out.println("fin");

} catch (Exception e) {

e.printStackTrace();

}

});

thread.start();

Thread.sleep(3000);

System.out.println("begin");

LockSupport.unpark(thread);

Thread.sleep(3000);

LockSupport.unpark(thread);

}

}

线程池

Executors 提供的线程池

newFixedThreadPool(threadNum) 最大与核心线程数量为threadNum的linked队列线程池

newSingleThreadExecutor()最大与核心线程数量为1的linked队列线程池

newCachedThreadPool 核心数量为0,最大线程数量为Integer.MAX_VALUE的线程池,线程数量会跟插入的任务同步增长

newSingleThreadScheduledExecutor() 核心数量为1,最大线程数量为Integer.MAX_VALUE的延迟队列线程池

newWorkStealingPool() ForkJoin线程池

ThreadPoolExecutor 自定义构造线程池

corePoolSize 核心线程数量

maxPoolSize 最大线程数量

keepAliveTime 超出核心线程数的线程的空闲存活时间

unit 时间单位

blockingQueue 存放未被执行的任务线程的阻塞队列,当队列满时,才会去从核心数量开始往最大线程数方向新增运行线程

threadFactory 线程创建工厂

rejectedHandler 任务满拒绝策略

ForkJoinPool

ForkJoinTask

RecursiveTask

RecursiveAction

并发容器

非并发容器转并发处理 Collections.synchronziedXXX()

其本质是在容器外层加了synchronized(mutex)

并发map - CpncurrentHashMap - 1.7 segment 1.8 synchronzied cas

并发ArrayList - RCU - read copy update - CopyOnWriteArrayList

并发Queue - ConcurrentLinkedQueue 非阻塞队列

阻塞队列 BlockingQueue - ReentrantLock Condition

ArrayBlockingQueue数组阻塞队列

LinkedBlockingQueue列表阻塞队列

SynchronousQueue此阻塞队列无容量立即转发

PriorityBlockingQueue优先级阻塞队列

DelayedWorkQueue延迟阻塞队列

原子类

AtomicBoolean

AtomicInteger

AtomicLong

LongAddr

AtomicRefrence

AtomicStampedRefreence

other

ThreadLocal线程本地变量

volatile可见关键字

Future非阻塞模式

CompletableFuture非阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值