JUC并发线程工具类

一. 集合安全类的使用
	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内存模型,不存在的东西,概念!约定!

  1. Volatile 是 Java 虚拟机提供轻量级的同步机制
    1、保证可见性
     	 对一个变量的修改会立即通知到其他线程上
    2、不保证原子性
    3、禁止指令重排 
    
 对于单线程而言,指令重排对结果无影响。 但是多线程中,会发生影响。 举例子: 如果一个线程初始化a,b的值。 另一个线程只要拿到b的值,就输出a。 那你想想,重排后,必然影响
	
	```
  1. 关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存。
2、线程加锁前,必须读取主存中的最新值到工作内存中
3、加锁和解锁是同一把锁
  1. 内存交互操作有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 进程号, 找到死锁问题,该指令可以定位到哪行代码

以上内容来自狂神

猜你喜欢

转载自blog.csdn.net/qq_34085100/article/details/110086991