并发编程
2021/4/27 20:25:26
本文主要是介绍并发编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
笔记
JUC并发编程1
1.线程和进程
进程:一个程序,QQ...等
一个线程往往可以包括多个线程,至少包含一个
java默认有几个线程?
两个 main和GC
线程:开了一个进程Typora,鞋子,自动保存(线程负责的)
对于java而言:Thread,Runnable,Callable
java真的可以开启线程吗?不可以
//本地方法,调用底层C++,java无法直接操作硬件 private native void start0();
2.并发和并行 并发编程 并发(多线程操作同一个资源-不同时刻同时运行): 单核CPU,模拟出来多条线程,天下武功,为快不破,快速交替 并行(多个人一起行走-同一时刻同时运行): 多核CPU,多个线程可以同时执行;线程池 3.Lock锁 传统Synchronize
Interface Lock 实现类: 可重入锁 读锁 写锁 ReentrantLock ReentrantReadWriteLock.ReadLock ReentrantReadWriteLock.WriteLock
公平锁:十分公平,可以先来后到
非公平锁:十分不公平,可以插队(默认)
Lock lock = new ReentrantLock(); 创建锁 lock.lock();加锁 业务 lock.unlock();解锁
Synchronized和Lock的区别 1.Synchronized 内置的java关键字,Lock是一个java类 2.Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁 3.Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,会产生死锁 4.Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);lock锁就不一定会等待下去
lock.tryLock(); 尝试获得锁5.Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平锁(可以自己设置) 6.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码 4.生产者消费者模式 生产者和消费和问题Synchronized版 判断等待 (业务) 通知 5.锁是什么?如何判断锁的是谁? 8锁问题...
class Phone{ /** * synchronize锁的对象是方法的调用者 * 两个方法用的是同一个锁,谁先拿到谁执行 */ public synchronized void sendSms() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("sendSms"); } public synchronized void call(){ System.out.println("call"); } }
public class Test2 { public static void main(String[] args) throws InterruptedException { Phone1 phone = new Phone1(); Phone1 phone2 = new Phone1(); 两个不同的对象,没有关系,分开执行,先输出call //锁的存在 new Thread(()->{ try { phone.sendSms(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone2.call(); },"B").start(); } } class Phone1{ /** * synchronize锁的对象是方法的调用者 * 两个方法用的是同一个锁,谁先拿到谁执行 */ public synchronized void sendSms() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("sendSms"); } public synchronized void call(){ System.out.println("call"); } //添加一个普通方法,没有锁 public void hello(){ System.out.println("hello"); } }
class Phone3{ /** * synchronize锁的对象是方法的调用者 * 两个方法用的是同一个锁,谁先拿到谁执行 * 添加一个static,锁的是class */ public static synchronized void sendSms() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("sendSms"); } public static synchronized void call(){ System.out.println("call"); } //添加一个普通方法,没有锁 public void hello(){ System.out.println("hello"); } }
public class Test3 { public static void main(String[] args) throws InterruptedException { Phone3 phone = new Phone3(); Phone3 phone2 = new Phone3(); //锁的存在 new Thread(()->{ try { phone.sendSms(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone2.call(); },"B").start(); } } class Phone3{ /** * synchronize锁的对象是方法的调用者 * 两个方法用的是同一个锁,谁先拿到谁执行 * 添加一个static,锁的是class 谁先拿到锁谁先执行 */ public static synchronized void sendSms() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("sendSms"); } public static synchronized void call(){ System.out.println("call"); } //添加一个普通方法,没有锁 public void hello(){ System.out.println("hello"); } }
public class Test4 { public static void main(String[] args) throws InterruptedException { Phone4 phone = new Phone4(); Phone4 phone2 = new Phone4(); //锁的存在 new Thread(()->{ try { phone.sendSms(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.call(); },"B").start(); } } class Phone4{ /** * synchronize锁的对象是方法的调用者 * 锁的不是同一个对象,先输出call,再输出sendSms * */ public static synchronized void sendSms() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("sendSms"); } //添加一个普通同步方法,没有锁 public synchronized void call(){ System.out.println("call"); } }
重点理解锁的对象是谁
6.集合类不安全
/** * 报错:ConcurrentModificationException */ public class ListTest { public static void main(String[] args) { // List<String> list = Arrays.asList("1","2","3"); // list.forEach(System.out::println); //不安全的集合 // List<String> list1 = new ArrayList<>(); // 解决方案 // 1.Vector // Vector<String> list1 = new Vector<>(); // 2.Collections.synchronizedxxx // List<String> list1 = Collections.synchronizedList(new ArrayList<>()); // 3.CopyOnWriteArrayList(写入时复制 COW) // 在写入的时候避免覆盖,造成数据问题 List<String> list1 = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list1.add(UUID.randomUUID().toString().substring(0,5)); // list1.forEach(System.out::println); System.out.println(list1); },String.valueOf(i)).start(); } } }
7.Callable(简单) 1.可以有返回值 2.可以排除异常 3.方法不同,run()/call() 减法计数器CountDownLatch
public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(7); for (int i = 0; i <= 6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "go to"); countDownLatch.countDown(); //数量减一 },String.valueOf(i)).start(); } countDownLatch.await(); //等待计数器归零,然后再向下执行 System.out.println("close door"); } }
加法计数器CyclicBarrier
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功!"); }); for (int i = 1; i < 8; i++) { final int temp = i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"收集了"+temp+"个龙珠"); try { cyclicBarrier.await(); //等待计数器达到要求 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
抢车位Semaphore
public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 7; i++) { new Thread(()->{ //得到 try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"抢占车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"离开车位"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放 semaphore.release(); } },String.valueOf(i)).start(); } } }
8.读写锁ReentrantReadWriteLock
public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 0; i < 6; i++) { final int temp = i; new Thread(()->{ myCache.put(temp+"",temp+""); }).start(); } for (int i = 0; i < 6; i++) { final int temp = i; new Thread(()->{ myCache.get(temp+""); }).start(); } } } /* 自定义缓存 */ class MyCache{ private volatile Map<String,Object> map = new HashMap<>(); //读写锁 private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //存,写(更加细粒度的控制) public void put(String key,Object value){ readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "写入" + key); map.put(key,value); System.out.println(Thread.currentThread().getName() + "写入OK"); }catch (Exception e){ e.printStackTrace(); }finally { readWriteLock.writeLock().unlock(); } } //取,读 public void get(String key){ readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "读取" + key); Object o = map.get(key); System.out.println(Thread.currentThread().getName() + "读取OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } }
JUC并发编程2
9.阻塞队列 BlockingQueue
什么情况下我们会使用阻塞队列:多线程并发处理,线程池
学会使用队列
添加,移除
四组API
1)抛出异常 add - remove
/** * 抛出异常 */ public static void test1(){ //队列的大小 ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.add("a"); blockingQueue.add("b"); blockingQueue.add("c"); // blockingQueue.add("d"); //FIFO 先进先出 System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); }
2)不会抛出异常 offer - poll
public static void test2(){ ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.offer("a"); blockingQueue.offer("b"); System.out.println(blockingQueue.offer("c")); //返回false,不抛出异常 System.out.println(blockingQueue.offer("d")); blockingQueue.poll(); //两种取出队首元素的方法 System.out.println(blockingQueue.peek()); System.out.println(blockingQueue.element()); blockingQueue.poll(); //返回c System.out.println(blockingQueue.poll()); //返回null,不抛出异常 System.out.println(blockingQueue.poll()); }
3)阻塞等待 put - take
public static void test3() throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); //队列没有位置,阻塞,一直等待 // blockingQueue.put("d"); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); }
4)超时等待 offer - poll 的重载方法
public static void test4() throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.offer("a"); blockingQueue.offer("b"); blockingQueue.offer("c"); //超时退出 blockingQueue.offer("d",2, TimeUnit.SECONDS); blockingQueue.poll(); blockingQueue.poll(); blockingQueue.poll(); blockingQueue.poll(2, TimeUnit.SECONDS); }
10.SynchronousQueue 同步队列
没有容量
进去一个元素,必须等待取出来之后,才能再往里面放一个元素
put,take
public class SynchronousQueueDemo { public static void main(String[] args) { //同步队列 BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+" put 1"); blockingQueue.put("1"); System.out.println(Thread.currentThread().getName()+" put 2"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName()+" put 3"); blockingQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+blockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); } }
11.线程池 池化技术 程序的运行,本质:占用系统的资源!优化资源的使用=>池化技术 线程池,连接池,内存池,对象池... 池化技术:事先准备好一些资源,有人要用就来拿,用完就还回去. 线程池的好处 1.降低资源的消耗 2.提高响应的速度 3.方便管理 线程复用,可以控制最大并发数,管理线程 线程池:三大方法,7大参数,四种拒绝策略 1.三大方法
//单个线程 Executors.newSingleThreadExecutor(); //创建一个固定的线程池的大小 // Executors.newFixedThreadPool(5); //可伸缩的线程池(可变) // Executors.newCachedThreadPool();
public class Demo1 { public static void main(String[] args) { //单个线程 // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //创建一个固定的线程池的大小 // ExecutorService threadPool = Executors.newFixedThreadPool(5); //可伸缩的线程池(可变) ExecutorService threadPool = Executors.newCachedThreadPool(); try { for (int i = 0; i < 100; i++) { //使用线程池之后,使用线程池来创建线程 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"OK"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
2.7大参数
public ThreadPoolExecutor(int corePoolSize, 核心线程池大小 int maximumPoolSize, 最大核心线程池大小 long keepAliveTime, 超时了没有人调用就会释放 TimeUnit unit, 超时的单位 BlockingQueue<Runnable> workQueue) { 阻塞队列 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), 线程工厂:创建线程的一般不用动 defaultHandler); 拒绝策略 }
3.四种拒绝策略
corePoolSize: 线程池维护线程的最少数量 maximumPoolSize:线程池维护线程的最大数量 keepAliveTime: 线程池维护线程所允许的空闲时间 unit: 线程池维护线程所允许的空闲时间的单位 workQueue: 线程池所使用的缓冲队列 handler: 线程池对拒绝任务的处理策略
1 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 2 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 3 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 4 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略: ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 最大线程到底该如何定义 1.CPU 密集型,几核就是几,保持Cpu的效率最高
Runtime.getRuntime().availableProcessors()2.IO 密集型 程序 15个大型任务 io十分占用资源 12.函数型接口 1.Function 函数型接口,只有一个方法的接口 参数类型T,返回类型R
public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); Function<String, String> function = (str)->{ return str; }; System.out.println(function.apply("ad"));
2.Predicate 断定型接口,有一个输入参数,返回值只能是->布尔值
public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t); public static void main(String[] args) { // Predicate<String> predicate = new Predicate<String>() { // @Override // public boolean test(String str) { // return str.isEmpty(); // } // }; Predicate<String> predicate = String::isEmpty; System.out.println(predicate.test("")); }
3.Consumer 消费性接口 有参数,没有返回值
public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); public static void main(String[] args) { // Consumer<String> consumer = new Consumer<String>() { // @Override // public void accept(String str) { // System.out.println(str); // } // }; // Consumer<String> consumer = (str)->{ // System.out.println(str); // }; Consumer<String> consumer = System.out::println; consumer.accept("oi"); }
4.供给型接口 没有参数,只有返回值
public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get();
13.流式计算
/** * 只能用一行代码实现 * 五个用户 * 1.ID 必须是偶数 * 2.年龄必须大于23 * 3.用户名转为大写 * 4.用户名字符倒序 * 5.只输出一个用户 */ public class Test { public static void main(String[] args) { User user1 = new User(1,"a",21); User user2 = new User(2,"b",22); User user3 = new User(3,"c",23); User user4 = new User(4,"d",24); User user5 = new User(6,"e",25); List<User> list = Arrays.asList(user1,user2,user3,user4,user5); list.stream() .filter(u->{return u.getId()%2==0;}) .filter(u->{return u.getAge()>23;}) .map(u->{return u.getName().toUpperCase();}) .sorted((uu1,uu2)->{return uu2.compareTo(uu1);}) .limit(1) .forEach(System.out::println); } }14.ForkJoin 什么是ForkJoin? ForkJoin在JDK1.7,并发执行任务!提高效率,大数据量 大数据:Map Reduce(把大人物拆分为小任务) ForkJoin的工作特点:工作窃取 A和B同时执行5个任务,当A执行完5个任务后,会把B没完成的任务偷过来执行,提高效率.
public class ForkJoin extends RecursiveTask<Long> { private Long start; private Long end; private Long temp = 10000L; public ForkJoin(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; ForkJoin task1 = new ForkJoin(start, middle); task1.fork(); //拆分任务,把任务压如线程队列 ForkJoin task2 = new ForkJoin(middle, end); task2.fork(); //拆分任务,把任务压如线程队列 return task1.join() + task2.join(); } } }
public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { test1(); //669 test2(); //10952 test3(); //503 } //普通方式 public static void test1(){ long sum = 0L; long start = System.currentTimeMillis(); for (long i = 1L; i <=10_0000_0000 ; i++) { sum += i; } long end = System.currentTimeMillis(); System.out.println("sum="+sum+"时间:"+(end-start)); } //使用ForkJoin public static void test2() throws ExecutionException, InterruptedException { // long sum = 0L; long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoin task = new ForkJoin(0L, 10_0000_0000L); // forkJoinPool.execute(task);//执行任务,没有返回值 ForkJoinTask<Long> submit = forkJoinPool.submit(task); Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println("sum="+sum+"时间:"+(end-start)); } //使用Stream public static void test3(){ long start = System.currentTimeMillis(); long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum); long end = System.currentTimeMillis(); System.out.println("sum="+sum+"时间:"+(end-start)); } }
15.异步回调
没有返回值的回调
/** * 异步执行:CompletableFuture * 成功回调 * 失败回调 */ public class Demo1 { public static void main(String[] args) throws Exception { //发起一个请求 //没有返回值的supplyAsync异步回调 CompletableFuture<Void> runAsync = CompletableFuture.runAsync(()->{ try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"runAsync=>Void"); }); System.out.println("111"); runAsync.get(); //获取阻塞执行结果 } }
有返回值的回调
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer"); int i=10/0; return 1024; }); System.out.println(completableFuture.whenComplete((t,u)->{ System.out.println("t->"+t); //正常的返回结果 System.out.println("u->"+u); //错误信息 }).exceptionally((e)->{ System.out.println(e.getMessage()); return 100; //可以获取到错误的返回结果 }));
16.JMM 请你谈谈你对Volatile的理解 Volatile是java虚拟机提供轻量级的同步机制 1.保证可见性 2.不保证原子性 3.禁止指令重排 什么是JMM? java内存模型,不存在的东西,概念,约定. 关于JMM的同步约定: 1.线程在解锁前,必须把共享变量立刻刷会主存 2.线程加锁前,必须读取主存中的最新值到工作内存中 3.加锁和解锁是同一把锁 线程 工作内存 主内存 8个操作(4组)
https://www.cnblogs.com/lewis0077/p/5143268.html
https://www.jianshu.com/p/8a58d8335270
17.Volatile
private static int num = 0; public static void main(String[] args) throws InterruptedException { new Thread(()->{ //线程1 对主内存的变化不知道 while (num == 0){ } }).start(); TimeUnit.SECONDS.sleep(1); num = 1; System.out.println(num); }
输出num为1,但是线程1没有停止,因为线程1不知道主内存的变化 保证可见性
private volatile static int num = 0; public static void main(String[] args) throws InterruptedException { new Thread(()->{ //线程1 对主内存的变化不知道 while (num == 0){ } }).start(); TimeUnit.SECONDS.sleep(1); num = 1; System.out.println(num); }加上volatile,使得内存可见,保证可见性 不保证原子性
private volatile static int num = 0; public static void add(){ num++; //不是一个原子操作 } public static void main(String[] args) { for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+"main "+num); }结果小于2万 使用原子类底层(AtomicXXX)AtomicInteger
// private volatile static int num = 0; private volatile static AtomicInteger num = new AtomicInteger(); public static void add(){ // num++; //不是一个原子操作 num.getAndIncrement(); //+1方法 } public static void main(String[] args) { for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+"main "+num); }
类的底层和操作系统相关,在内存中修改值!Unsafe类是一个很特殊的类 指令重排 什么是指令重排:你写的程序,计算器并不是按照你写的那样去执行的. 源代码->编译器优化的重排->指令并行也可能会重排->内存系统也会重排->执行 volatile可以避免指令重排 1.保证特定的操作和执行顺序 2.可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
volatile可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
18.玩转单例模式
//饿汉式单例 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]; private Hungry(){ } private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){ return HUNGRY; } }
//懒汉式单例 public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+"OK"); } // private static LazyMan lazyMan; private volatile static LazyMan lazyMan; // public static LazyMan getInstance(){ // if(lazyMan==null){ // lazyMan = new LazyMan(); // } // return lazyMan; // } //双重检测锁模式的懒汉式单例 DCL懒汉式 public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个空间 * */ } } } return lazyMan; } //多线程并发 public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } }
//静态内部类 public class Holder { private Holder(){ } private static Holder getInstance(){ return InnerClass.HOLDER; } public static class InnerClass{ private static final Holder HOLDER = new Holder(); } }
单例模式 | 菜鸟教程 (runoob.com)
19. 深入理解CAS
什么是CAS?
public class CASDemo { // CAS public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2021); //public final boolean compareAndSet(int expect, int update) { //如果期望的值达到了,就更新,否则不更新 atomicInteger.compareAndSet(2021,2022); System.out.println(atomicInteger); System.out.println(atomicInteger.get()); } }
CAS:比较当前工作内存中的值和主内存中的值,如果个值是期望的,那么久执行操作,如果不是就一直循环
缺点:
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.ABA问题
CAS:ABA问题(狸猫换太子)
// CAS public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2021); //public final boolean compareAndSet(int expect, int update) { //如果期望的值达到了,就更新,否则不更新.CAS是cpu的并发源语 //捣乱的线程 System.out.println(atomicInteger.compareAndSet(2021, 2022)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2022, 2021)); System.out.println(atomicInteger.get()); //期望的线程 System.out.println(atomicInteger.compareAndSet(2021, 111)); System.out.println(atomicInteger.get()); }
20.原子引用 带版本号的原子操作 解决ABA问题,引入原子引用(乐观锁)
// CAS public static void main(String[] args) { // AtomicInteger atomicInteger = new AtomicInteger(2021); AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10, 1); new Thread(()->{ int stamp = atomicStampedReference.getStamp(); //获得版本号 System.out.println("A1:"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(10, 20, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("A2:"+atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(20, 10, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("A3:"+atomicStampedReference.getStamp()); },"A" ).start(); new Thread(()->{ int stamp = atomicStampedReference.getStamp(); //获得版本号 System.out.println("B1:"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(10, 66, stamp, stamp + 1)); System.out.println("B2:"+atomicStampedReference.getStamp()); },"B" ).start();
21.各种锁的理解 1.公平锁,非公平锁 公平锁:非常公平,不能够插队,必须先来后到. 非公平锁:非常不公平,可以插队 默认是非公平锁(源码)
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
new ReentrantLock(true)
2.可重入锁 可重入锁(递归锁) 拿到外面的锁,就相当于拿到了里面的锁 1.synchronized版
public class Demo1 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); } } class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName()+"sms"); call();//这里也有锁 } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"call"); } }
固定结果
Asms Acall Bsms Bcall
2.lock版:lock锁必须配对
public class Demo2 { public static void main(String[] args) { Phone1 phone = new Phone1(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); } } class Phone1{ private Lock lock = new ReentrantLock(); public synchronized void sms(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "sms"); call();//这里也有锁 }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public synchronized void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName()+"call"); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } }
3.自旋锁Spinlock => Unsafe
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
// 自旋锁 public class SpinlockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); // 加锁 public void myLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName()+"=>>myLock"); //自旋锁 while (!atomicReference.compareAndSet(null,thread)){ } } //解锁 public void myUnlock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName()+"=>>myUnLock"); atomicReference.compareAndSet(thread,null); } } class TestSpinLock{ public static void main(String[] args) { SpinlockDemo lock = new SpinlockDemo(); new Thread(()->{ try { lock.myLock(); TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.myUnlock(); } },"A").start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ try { lock.myLock(); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.myUnlock(); } },"B").start(); } } 输出结果:只有A解锁,B才有资格解锁 A=>>myLock B=>>my
4.死锁
1.jps -l 定位进程号
2.jstack 进程号 找到死锁问题
这篇关于并发编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Java语音识别项目资料:新手入门教程
- 2024-11-26JAVA语音识别项目资料:新手入门教程
- 2024-11-26Java语音识别项目资料:入门与实践指南
- 2024-11-26Java云原生资料入门教程
- 2024-11-26Java云原生资料入门教程
- 2024-11-26Java云原生资料:新手入门教程
- 2024-11-25Java创意资料:新手入门的创意学习指南
- 2024-11-25JAVA对接阿里云智能语音服务资料详解:新手入门指南
- 2024-11-25Java对接阿里云智能语音服务资料详解
- 2024-11-25Java对接阿里云智能语音服务资料详解