-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 16.1 KB
/
content.json
1
[{"title":"悲观锁和乐观锁","date":"2020-05-10T14:56:00.000Z","path":"2020/05/10/悲观锁和乐观锁/","text":"悲观锁和乐观锁并发:         首先你要理解并发和多线程不是一个概念,多线程指的是单个程序中同时运行多个线程完成不同的工作,称为多线程,并发指的是一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任意一个时刻点上只有一个程序在处理机上运行,此外还有一个并行,他指的是两个或者多个事件在同一时刻发生 ,并发强调的时间段,更准确的来说并发是不断的互相抢占; 悲观锁( Pessimistic Concurrency Control PCC)       嗯你可以认为他有被害妄想症,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁 ,悲观的并发控制实际上是先锁在访问的保守策略,因而它的效率有些不尽人意,这种加锁会产生额外的开销,同时降低了并行性,Java中synchronized(内置)和ReentrantLock(重入锁)等独占锁就是悲观锁思想的实现, AQS框架下的锁则是先尝试CAS乐观锁去获取锁,获取不到,才会转换为悲观锁 ; synchronized和Lock的异同]https://www.cnblogs.com/gejuncheng/p/10777691.html 乐观锁(Optimistic Lock )       即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟拿数据时的版本号,如果一样则更新,版本号+1),乐观锁基本都是通过CAS操作实现的这在之前的java8(1)中有介绍,JDK1.5 中新增java.util.concurrent(J.U.C)就是建立在CAS之上。相比较于synchronized这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升但是这种锁也有问题。 ABA: 如果一开始位置V得到的旧值是A,赋值时再次读取仍然是A,并不能说明变量没有被其它线程改变过。有可能是其它线程将变量改为了B,后来又改回了A,大部分情况下不会影响并发的正确性,如果要解决这种问题,用传统的互斥同步可能比原子类更高效; 自旋时间长开销大: 自旋CAS(也就是不成功就一直循环直到成功)但是长时间不成功会给CPU带来非常大的执行开销,如果JVM支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令,使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是0.第二他可以避免在退出循环时因内存顺序冲突引起CPU流水线被清空,从而提高CPU执行效率; 只保证单个共享变量: CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效,但是从JDK1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作 : Mysql(分布式) 悲观锁主要是共享锁和排他锁, 适合写操作多的场景 ;乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。 表级锁( MyISAM 支持):开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低。行级锁( InnoDB 支持):开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也最高。页面锁:开销和加锁时间界于表锁和行锁之间,会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般 行级锁:       共享锁(s):又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改       排他锁(x):又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改 数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,“串行化”与“并发”是矛盾的 总结:       对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能       对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized       synchronized 在JDK1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入了偏向锁和轻量级锁以及其他各种优化之后变得在某些情况下并不是那么重了,底层实现主要依靠lock-Free队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能,而线程冲突严重的情况下性能远高于CAS","tags":[{"name":"java","slug":"java","permalink":"http://yoursite.com/tags/java/"},{"name":"锁","slug":"锁","permalink":"http://yoursite.com/tags/%E9%94%81/"}]},{"title":"JAVA8_2","date":"2020-05-09T11:57:50.000Z","path":"2020/05/09/JAVA8-2/","text":"JAVA8(2)函数式接口         函数式接口就是只有一个抽象方法的的接口,可以使用@FunctionInterface修饰,它可以检查这个接口是否是函数式接口 函数式接口 参数类型 返回类型 用途 BiFuction<T,U,R> T,U R 对类型为T,U参数应用操作,返回R类型的的结果包含方法为R apply(T t,U u); UnaryOperator(Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果,包含方法T apply(T t) BinaryOperator(BiFuction子接口) T,T T 对类型为T,U的对象进行二元运算,并且返回T类型的结果,包含方法T apply(T t1,T t2) BiConsumer<T,U> T,U void 对类型为T,U参数应用操作包含方法为 void accpet(T他,U u) ToIntFunction ToLongFunction ToDoubleFunction T int Long double 分别计算 int、long 、 double值的函数 IntFunction LongFunction DoubleFunction int Long double R 参数分别为int、long、double类型的参数 新时间API &ensp; &ensp; &ensp; 在java8之前我们处理时间Date、SimpleDateFormat、Calendar都存在线程不安全的问题,而且繁琐在java8中,他选择了一个优秀的时间类[Joda-Time]( https://www.ibm.com/developerworks/library/j-jodatime/index.html )计算事件运行的时间: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556//旧时间long start =System.currentTimeMillis(); //事件 long end =System.currentTimeMillis(); System.out.println(start-end); //java8中 Instant start = Instant.now(); //事件 Instant end = Instant.now(); Duration time = Duration.between(start, end); //毫秒long s=time.getSeconds();//秒long ms = time.toMillis();/*比较两个时间的间隔*///第一个时间Instant start = Instant.now();Instant end = Instant.now();Duration time = Duration.between(start, end);//第二个时间Instant start1 = Instant.now();Instant end1 = Instant.now();Duration time1 = Duration.between(start1, end1);//public boolean isNegative() 持续时间的总长度小于零,则为true,大于零false System.out.println(time.minus(time1).isNegative());//获取本地的时间和日期LocalDate dateNow = LocalDate.now();//根据年月日取日期:LocalDate dateStr = LocalDate.of(2019, 1, 1); // -> 2019-01-01//根据字符串取:LocalDate endOfFeb = LocalDate.parse(\"2020-01-01\"); // 严格按照ISO8601 yyyy-MM-dd验证LocalTime timeNow = LocalTime.now();LocalTime time = LocalTime.of(22, 50, 56);//时间加算 HOURS可替换为年 月 周 日 时 分 秒 毫秒。。。 LocalTime.now().plus(1, ChronoUnit.HOURS);//等效于 plusHours(1)//时间格式化String data1 = dateNow.format(DateTimeFormatter.BASIC_ISO_DATE); // yyyMMddString data2 = dateNow.format(DateTimeFormatter.ISO_LOCAL_DATE); // yyyy-MM-ddString data3 = dateNow.format(DateTimeFormatter.ISO_LOCAL_TIME); // HH-mm-ss-msString data4 = dateNow.format(DateTimeFormatter.ofPattern(\"yyyy-MM-dd\")); // 指定格式(yyyy-MM-dd)String data5 = dateNow.format(DateTimeFormatter.ofPattern(\"今天是:YYYY年 MMMM dd日 E\", Locale.CHINESE)); // 今天是:yyyy年 MM月 dd日 星期//标准格式化DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);String data6 = formatter.format(LocalDateTime.now());//查看当前的时区 Asia/ShanghaiZoneId defaultZone = ZoneId.systemDefault();//查看美国纽约当前的时间ZoneId usa = ZoneId.of(\"America/New_York\");LocalDateTime shanghai = LocalDateTime.now();LocalDateTime usaTime = LocalDateTime.now(usa);//带有时区的时间ZonedDateTime tiemZone = ZonedDateTime.now(defaultZone);System.out.println(tiemZone); //2020-01-01T11:03:05.926+08:00[Asia/Shanghai] 方法 说明 返回 date.isBefore(date1) 是否在之前 Boolean date.isAfter(date1) 是否在之后 Boolean date.until(today, ChronoUnit.DAYS) 计算时间差,单位可替换 时间差       转换中需要注意,java8之前Date是包含日期和时间的,而LocalDate只包含日期,LocalTime只包含时间,所以与Date在互转中,必定会丢失日期或者时间,或者会使用起始时间。如果转LocalDateTime ,详细的可以看这篇文档 https://iowiki.com/javatime/javatime_index.html 优秀的你今天别忘记分享奥!!!","tags":[{"name":"java","slug":"java","permalink":"http://yoursite.com/tags/java/"}]},{"title":"JAVA 8","date":"2020-05-08T15:37:37.000Z","path":"2020/05/08/JAVA-8/","text":"JAVA8(1)概述:         是什么让我选择了java8,是命运吗?是爱吗?🤮,好吧我自己也编不下去了,其实最先关注java8是因为Lambda和stream API,然后才了解到了他的一系列的数据结构和底层的优化,而且还是长期支持的版本,so你懂的! 更快的速度:         效率和速度这是一个永远无法避免的问题,以前的底层结构z中方法区(堆中永久区的一部分,用于存放核心类库的信息,几乎不会被GC回收)总是被单独拎出来,因为一些厂商像sun公司的Hostpot他的JVM存在永久区而另外像IBM等一些公司已经移除了这一区域,在java8之后他统一取消了这一区域,出现Metaspace(元空间),他和方法区的不同是他直接用的是物理内存发生内存溢出(out of memory error OOM)的异常发生的机率降低,同时永久区的改变使得JVM调优中的PremGenSize 、MaxPremGenSize 无效 ,取而代之的是MetaspaceSize和MaxMetaspaceSize; 数据结构的改变         可能对于你来说这些不足以打动你但是,同时java8也对一些数据结构进行了改编,例如常用的HashMap,哈希算法因为碰撞会导致效率变低,在java8中他在原有的数组+链表的结构上引入了红黑树,这是一种自平衡的二叉查找树( symmetric binary B-trees ),除了添加都非常快。         Java7的ConcurrentHashMap中通过Segment引入了分段加锁机制用来解决并发中HashMap可能导致的死循环问题。而Java8又对ConcurrentHashMap进行了改动,除了引入红黑树外,还去除了Segment,那么在ava8是如何保证线程安全的呢?         其实他内部采用了CAS( compare and swap )+ Synchronized ,锁的粒度: 原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)         CAS这是一种基于锁的操作,而且是乐观锁,在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。操作包含三个操作数 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行 lambda表达式         他是一个匿名的函数,可以理解为一段可以传递的代码(将代码像数据一样进行传递)。可以写出跟简洁、灵活的代码。作为一种更紧凑的代码风格,使java的语言表达能力得到了提升。 12345678List<User> list=Arrays.asList(new User(1L,\"zs\",\"male\"), new User(2L,\"ls\",\"male\"), new User(2L,\"ls\",\"female\") ); for (Object obj:list){ System.out.println(obj.toString()); } list.parallelStream().forEach(ls-> System.out.println(ls)); stream API         上面同时展现了一个stream,什么是stream(流)?他其实是对于集合的计算,stream本身不会存储数据,改变源数据,他的操作是延迟的,也就是需要的时候才会执行。         他其实是java7的Fork/Join,是一个并行执行任务框架 ,这一个框架会将任务拆分成若干小任务,然后连接结合,进行结果汇总。 ForkJoin采用了工作窃取(work-stealing)算法,若一个工作线程的任务队列为空没有任务执行时,便从其他工作线程中获取任务主动执行。为了实现工作窃取,在工作线程中维护了双端队列,窃取任务线程从队尾获取任务,被窃取任务线程从队头获取任务。这种机制充分利用线程进行并行计算,减少了线程竞争。但是当队列中只存在一个任务了时,两个线程去取反而会造成资源浪费。 常用的一些方法: 方法 说明 parallelStream() 并行流 stream() 串行流 中间操作 方法 说明 fileter (Predicate p) 过滤 distinct() 通过流元素产生的hashcode和equals去除重复元素 limit(long maxSize) 截断,使其不超过最大值 skip(long n) 跳过元素 map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素,并映射成新的元素 mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素,并产生一个新的DoubleStream sort() 按自然顺序排序(字典顺序) sorted(Comparator c) 按照比较器排序 终止操作 方法 说明 allMatch(Predicate p) 检查是否匹配所有元素 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配的元素 findFirst() 返回第一个元素 findAny() 返回任意当前流中的元素 count() 返回流中数据的元素个数 max(Comparator c) 返回流中最大的值 min(Comparator c) 返回流中最小的值 forEach(Consumer c) 内部迭代 reduce(T iden , BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回T reduce(BianaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 collect(Collector c) 结果转换 Optional 类型         这是一个允许结果为空值的对象,从而减少了空指针的发生,例如对数据进行过滤的时候常常没有满足条件的数据这个时候就可以使用Optional","tags":[{"name":"java","slug":"java","permalink":"http://yoursite.com/tags/java/"}]}]