补充之前的学习笔记
1JUC是什么
1.1 Java.util.concurrent =JUC
1.2 进程:系统里运行的多个程序QQ.exe 线程:一个进程中有多个线程
1.3 线程的多种状态。
.start()–就绪状态
State:
new创建 runnable启动 blocked阻塞 waiting等待(不见不散) timed_waiting等待(过时不候) terminated终结
1.4 wait交锁 sleep不交锁
1.5 并发:同一时间点多个线程访问同一个资源 并行:同时执行多个资源
2、lambda表达式
①写法:拷贝中括号+写死右箭头+落地大括号
②只有函数接口(接口里只有一个方法时)才能用lambda表达式
③接口上标记@FunctionalInterface
Foo foo = () -> {业务逻辑代码,实现方法}
④default方法的实现
用@FunctionalInterface的接口只能有个一方法,但是可以又多个default方法
⑤静态方法实现
用@FunctionalInterface的接口只能有个一方法,但是可以又多个default方法,可以有多个静态方法
3、线程间的通信
3.1生产者+消费者
3.2通知等待唤醒机制
判断 干活 通知
3.3 2个线程变4个线程,禁止出现虚假唤醒,判断条件用while
3.4
—————-lock—————–
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
condition.awati(); codittion.signalAll();
——————-syncronised—————–
| |
wait(); notifiyAll();
4、传值和传引用
基本类型传复印件
引用类型传引用
String类型比较特殊,因为有个池的概念,所以相当于指向变了,但是原来的指针还是指向原来的引用
5、Callable
callable:有异常、有返回值、call
runnable:无异常、无返回值、run
FutureTask作用:异步调用
自顶向下,逐步求精
一个futuretask被多个线程调用,结果可以复用
futureTask.get()只允许放到最后,get方法只计算一次
—————–原理,底层——————
①在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过future对象获得后台作业的计算结果或者执行状态。
②一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果
③仅再计算完成时才能检索结果,如果计算尚未完成,则阻塞get方法,一旦计算完成,就不能再重新开始或者取消计算,get方法获取结果只有再计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。
6、ReadWriteLock
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock ();
rwLock .writeLock().lock();
rwLock .readLock().lock();
读写锁案例:读可共享,写排他
class ReadWrite{ private Object obj; private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void writeLock(Object obj){ rwLock.writeLock().lock(); try { this. obj = obj; System. out.println(Thread. currentThread().getName()+”写线程正在执行\t”+obj); } catch (Exception e) { // TODO: handle exception } finally{ rwLock.writeLock().unlock(); } }
public void readLock(){ rwLock.readLock().lock(); try { System. out.println(Thread. currentThread().getName()+”读线程正在执行\t”+obj); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ rwLock.readLock().unlock(); } }
} |
7、线程池
ExecutorService service=Executors.newFixedThreadPool(5);//一池5线程(核心线程=最大线程=5)
阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor( nThreads, nThreads , 0L, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } |
ExecutorService service=Executors.newSingleThreadExecutor();//一池1线程(核心线程=最大线程=1)
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor(1, 1, 0L, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } |
ExecutorService service=Executors.newCachedThreadPool();//一池N线程(核心线程0,最大线程int的最大值相当于没有上限)
它是一个可以无限扩大的线程池;
它比较适合处理执行时间比较小的任务;
corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
public static ExecutorService newCachedThreadPool () { return new ThreadPoolExecutor(0, Integer. MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>()); } |
service.submit(Runnable);
service.shutdown();
———————————
ScheduledExecutorService service=Executors.newScheduledThreadPool(5);//时间轮询,每隔多少时间执行一个任务,如果线程忙不过来会自动新加线程(核心线程5,最大线程int最大值相当于没有上限)
它采用DelayQueue存储等待的任务
DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序,若time相同则根据sequenceNumber排序;
DelayQueue也是一个无界队列;
工作线程的执行过程:
工作线程会从DelayQueue取已经到期的任务去执行;
执行结束后重新设置任务的到期时间,再次放回DelayQueue
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor( corePoolSize); } public ScheduledThreadPoolExecutor( int corePoolSize) { super(corePoolSize, Integer. MAX_VALUE, 0, TimeUnit. NANOSECONDS, new DelayedWorkQueue()); } public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors. defaultThreadFactory(), defaultHandler); } |
service.schedule(callable,delay,unit);//线程,延迟,时间单位 每隔2s提交一次请求
public class ReadWriteLock { public static <E> void main(String[] args) {
ExecutorService service = Executors. newCachedThreadPool();
final ReadWrite readWrite = new ReadWrite();
try { Thread thread1 = new Thread( new Runnable() { @Override public void run() { readWrite.writeLock( “this is write”);
} }, “AA”); service.execute( thread1);
for( int i=1; i<=100; i++){ Thread thread2 = new Thread( new Runnable() { @Override public void run() { readWrite.readLock(); } }, String. valueOf(i)); service.execute( thread2); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ service.shutdown(); } } } |
8、常用工具类
CountDownLatch—-所有线程执行完才执行的任务(秦灭六国,一统华夏)
final CountDownLatch countDownLatch = new CountDownLatch(6);
countDownLatch .countDown();//没执行一条就-1,直到6条都执行完
countDownLatch .await();//阻塞最后一个要执行的主线程
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException { //CountDownLatch final CountDownLatch countDownLatch = new CountDownLatch(6);
for( int i=1; i<=6; i++){ new Thread( new Runnable() {
@Override public void run() { System. out.println(Thread. currentThread().getName()+”\t 国家被灭”); countDownLatch.countDown(); } }, CountryEnums.forEachCountryEnums(i).getMsg()).start(); }
countDownLatch.await(); System. out.println(Thread. currentThread().getName()+”\t 秦灭六国,一统华夏” ); } } |
CyclicBarrier—集齐7颗龙珠,可以召唤神龙(其他线程执行完了只能等待)
public class CyclicBarrierDemo {
private final static int number=7;
public static void main(String[] args) { final CyclicBarrier cyclicBarrier = new CyclicBarrier(number, new Runnable() { @Override public void run() { System. out.println( “集齐7颗龙珠,可以召唤神龙” ); } }); for( int i=1; i<=7; i++){ final int temp= i; new Thread( new Runnable() { @Override public void run() { try { System. out.println(Thread. currentThread().getName()+”\t 收集第”+ temp+ “颗龙珠”); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }, String. valueOf(i)).start(); }
} } |
Semaphore—-信号灯(争车位)
public class SemaphoreDemo { public static void main(String[] args) { final Semaphore semaphore = new Semaphore(3); //模拟3个停车位 for( int i=1; i<=6; i++){ //模拟6个汽车 new Thread( new Runnable() {
@Override public void run() { try { semaphore.acquire(); System. out.println(Thread. currentThread().getName()+ “\t 抢占到停车位” ); TimeUnit. SECONDS.sleep( new Random().nextInt(5)); System. out.println(Thread. currentThread().getName()+ “\t—- 离开了停车位” ); } catch (InterruptedException e) { e.printStackTrace(); } finally{ semaphore.release(); }
} }, String. valueOf(i)).start(); } } } |
9、集合不安全类
ArrayList、HashMap、HashSet
java.util.ConcurrentModificationException
CopyOnWriteArrayList();//写时复制
往元素中添加元素时,先复制一份新的数组(Arrays.copyOf),长度+1,把要添加的元素添加到新的数组中。最后把引用指向新的数组。(整个过程添加了reentrainLock)
CopyOnWriteArraySet<String>();
ConcurrentHashMap<>();
sss—->Arrays Collections
三者对比:
1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行; 而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行; 另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。 2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。 |
10、volatile
内存可见性
public class SafeSingletonDemo { private static volatile SafeSingletonDemo safeSingletonDemo = null; private SafeSingletonDemo(){ System. out.println( “********”+Thread. currentThread().getName()); }
//double check lock public static SafeSingletonDemo getInstance(){ if( null == safeSingletonDemo){ synchronized (SafeSingletonDemo. class) { if( null == safeSingletonDemo){ safeSingletonDemo= new SafeSingletonDemo(); } } } return safeSingletonDemo; }
public static void main(String[] args) { new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, “AA”).start(); new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, “BB”).start(); new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, “CC”).start(); } } |
彩蛋:某次公开课中记录的笔记
击穿缓存的方法:
口诀:读多写少用缓存,写多读少用队列,限流、分流
方法1:化并发为同步
lock:等待锁:粗粒度的锁
1个线程拿到锁,重建缓存,
其他1999个线程等待,从redis取
方法2:互斥锁ConcurrentHashMap<> map 细粒度的锁
车次号1–>是否有锁
车次号2–>是否有锁
boolean lock = false;
lock = map.putIfAbsent(key,value)==null;//代表当前没有数据,不为null,当前有数据
if(lock){//拿到锁
//重建缓存
//再查一次
}else{//没拿到锁
//缓存降级
1:提示:当前人数太多,请耐心等待
}