格子三合一交友婚恋网站模板,做pc端网站哪家好,国产免费crm系统有哪些,做经营性的网站需要注册什么目录 1.JUC并发编程的核心类
2.TimeUnit#xff08;时间单元#xff09;
3.原子操作类
4.CAS 、AQS机制 1.JUC并发编程的核心类
虽然java中的多线程有效的提升了程序的效率#xff0c;但是也引发了一系列可能发生的问题#xff0c;比如死锁#xff0c;公平性、资源管理…目录 1.JUC并发编程的核心类
2.TimeUnit时间单元
3.原子操作类
4.CAS 、AQS机制 1.JUC并发编程的核心类
虽然java中的多线程有效的提升了程序的效率但是也引发了一系列可能发生的问题比如死锁公平性、资源管理以及如何面对线程安全性带来的诸多危害。为此java就提供了一个专门的并发编程包java.util.concurrent简称JUC。此包能够有效的减少了竞争条件和死锁问题。
以下介绍JUC包中核心的类
类名描述ExecutorExecutor 是一个接口定义了一种执行任务的方式其目的是将任务的提交与任务的执行解耦。ExecutorServiceExecutorService 是 Executor 的子接口提供了更丰富的功能例如线程池管理和任务提交等。ScheduledExecutorServiceScheduledExecutorService 是 ExecutorService 的子接口可以按照计划时间或延迟来执行任务。CompletionServiceCompletionService 是一个用于异步执行任务并获取已完成任务结果的框架。CallableCallable 是一个代表可以返回结果或抛出异常的任务的接口。它类似于 Runnable 接口但具有返回值。FutureFuture 是一个可用于获取异步计算结果的接口。ReentrantLockReentrantLock 是一个可重入锁它提供了更灵活的同步控制和更高级别的功能。BlockingQueueBlockingQueue 是一个支持阻塞操作的队列提供了线程安全的生产者-消费者模式的实现。CountDownLatchCountDownLatch 是一个同步辅助类允许一个或多个线程等待其他线程完成操作后再继续执行。CyclicBarrierCyclicBarrier 是一个同步辅助类使得一组线程能够互相等待直到所有线程都达到某个公共屏障点。 2.TimeUnit时间单元
这个类能够非常好的让我们实现各种时间之间的转换。TimeUnit类的是枚举类里面有DAYS天,HOURS小时,MINUTES分钟,SECONDS秒,MILLISECONDS毫秒,NANNOSECONDS纳秒
TimeUnit类中常用的方法
方法签名描述public long convert(long sourceDuration, long srcDuration)该方法用于将给定的时间源持续时间转换为目标持续时间。public void sleep(long timeout) throws InterruptedException该方法使当前线程进入休眠状态暂停执行一段指定的时间以毫秒为单位。如果在休眠期间中断了线程则会抛出 InterruptedException 异常。
具体应用案例
1.时间转换与输出一个月后的日期
package Example2101;import java.util.Date;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {
// 五个小时时间long hours 5;
// 通过SECONDS类将5个小时转为秒long seconds TimeUnit.SECONDS.convert(hours,TimeUnit.HOURS);System.out.println(seconds);// 获取当前时间long now System.currentTimeMillis();long furture now TimeUnit.MILLISECONDS.convert(30,TimeUnit.DAYS);System.out.println(Now Time isnew Date(now));Date futureDay new Date(furture);System.out.println(after mounth time isfutureDay);}
}案例2定义一个闹钟这个闹钟在5天后会自动发送消息 这种闹钟形式可以通过线程的睡眠机制进行完成但是一般情况下如果使用线程的睡眠Thread.sleep()里面放的是毫秒如果要睡眠五天那么需要设置的数值会非常非常大的所以可以使用TimeUnit类的睡眠方法实现自定义睡眠。 package Example2102;import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {new Thread(()-{try {
// 通过TimeUnit下的Days类的sleep函数定义五天时间TimeUnit.DAYS.sleep(5);System.out.println(闹钟响了);}catch (InterruptedException e){e.printStackTrace();}},闹钟).start();}
}3.原子操作类
问题引出一般情况下如果多线程进行竞争一个变量时候会引发数据错乱的问题。比如多线程下售票员售票案例由于多个线程竞争一张票可能已经被卖出去了但是其他的售票员并不知道继续售卖同一张票。在之前的时候我们通过了Sychronized同步位解决了这个问题。但是用这个方法也有不小的弊端那就是程序效率会大大下降。为此JUC提供了一个新的方式解决这个问题那就是原子操作类。
首先理解原子性原子是不可分割的最小物体在编程中是指一种操作要么做了要么不做。不可以中断的一种操作。原子操作类具有更高效率更安全更简单用法 原子操作类分为很多类大致分为4类 基本类型AtomicInteger 、AtomicLong、AtomicBoolean 数组类型AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 引用类型AtomicReference、AtomicStampedReference、AtomicMarkableReference 对象属性修改类型 AtomicIntegerFieldUpdaterAtomicLongFiledUpdaterAtomicReferenceFieldUpdater 1.基本类型的原子操作类
基本类型AtomicInteger 、AtomicLong、AtomicBoolean
基本类型之间的操作是差不多的这里用AtomicLong举例
AtomicLong的常用方法
方法描述AtomicLong(long initValue)创建一个新的AtomicLong实例并设置初始值为initValue。get()获取当前存储在AtomicLong中的值。set(long newValue)将AtomicLong的值设置为newValue。getAndIncrement()先获取当前存储在AtomicLong中的值然后将AtomicLong的值增加1。返回先前的值。setAndIncrement()将AtomicLong的值增加1。返回增加前的值。decrementAndGet()将AtomicLong的值减少1并返回减少后的值。
使用类方法的关键就在于熟悉add增加 decrement自减increment(自增) set设置值 get获取类内部的数据 方法就是这几个操作之间的组合
案例代码多个售票员售卖100张票
package Example2103;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class javaDemo {public static void main(String[] args) {
// 创建原子操作类AtomicInteger ticket new AtomicInteger(6);AtomicInteger flag new AtomicInteger(1);//标志还有票// 创建三个线程进行售票for (int i 0;i3;i){new Thread(()-{while (ticket.get()0){System.out.println(售票员Thread.currentThread().getName()售卖第ticket.decrementAndGet()张票);try {
// 设置两秒睡眠TimeUnit.SECONDS.sleep(2);}catch (Exception e){e.printStackTrace();}
// 如果没有票了就将标志位的值设置为0表示没有票了if (ticket.get() 0){flag.set(0);System.out.println(卖完了);}}}).start();}}
}可以看到即使没有使用同步机制也实现了同步的效果。
2.数组原子操作类
数组原子操作类有AtomicArrayInteger AtomicLongArray AtomicReferenceArray对象数组
由于三者这件的使用区别不大所以这里展示AtomicReferenceArray
AtomicReferenceArray常用方法
方法描述AtomicReferenceArray(int length)构造一个指定长度的AtomicReferenceArray对象。AtomicReferenceArray(E[] array)使用给定数组初始化AtomicReferenceArray对象。int length()返回AtomicReferenceArray的长度即元素个数。boolean compareAndSet(int index, E expect, E update)将指定索引位置的元素与期望值进行比较如果相等则将其更新为新的值。该操作是原子性的返回是否更新成功。E get(int index)获取指定索引位置的元素的值。void set(int index, E newValue)设置指定索引位置的元素的值为newValue。E getAndSet(int index, E newValue)获取指定索引位置的元素的当前值并将其设置为newValue。 案例代码
package Example2104;import java.util.concurrent.atomic.AtomicReferenceArray;public class javaDemo {public static void main(String[] args) {String data[] new String[]{王二狗,180,130};
// 初始化AtomicReferenceArrayString array new AtomicReferenceArrayString(data);
// 对象数组的操作System.out.println(身高是:array.get(1));array.set(2,150);System.out.println(array.get(0)在拼命锻炼后体重变成:array.get(2));
// 筛选如果名字是王二狗的自动改名王二array.compareAndSet(0,王二狗,王二);System.out.println(改名后名字叫array.get(0));}
}3.引用原子操作类
引用类型AtomicReference、AtomicStampedReference、AtomicMarkableReference
其中AtomicReference是可以直接引用数据类型的原子性操作
下面是AtomicReference的常用方法
方法描述AtomicReference()无参构造方法创建一个初始值为null的AtomicReference对象。V get()获取当前AtomicReference对象持有的值。void set(V newValue)设置AtomicReference对象的值为newValue。boolean compareAndSet(V expect, V update)将AtomicReference对象的值与期望值expect进行比较比较如果相等则将其更新为新值update。该操作是原子性的返回是否更新成功。V getAndSet(V newValue)先获取当前AtomicReference对象的值然后将其设置为newValue并返回原来的值。
案例代码使用AtomicReference进行引用操作
package Example2106;import java.util.concurrent.atomic.AtomicReference;
// 创建普通人类
class Person{private int age;private String name;private int id;Person(int age,String name,int id){this.age age;this.name name;this.id id;}
}public class javaDemo {public static void main(String[] args) {Person person1 new Person(18,张三,001);Person person2 new Person(20,王思,1002);
// 传入person1对象AtomicReferencePerson person new AtomicReferencePerson(person1);
// 输出对象地址System.out.println(person.get());
// 更改引用对象person.set(person2);System.out.println(person.get());}
}AtomicStampedReference 基于版本号的数据引用。其中版本号是自己定义的int数据类型
下面是AtomicStampedReference的常用方法
方法描述AtomicStampedReference(V initRef, int initStamp)构造一个AtomicStampedReference对象初始引用值为initRef初始标记值戳为initStamp。V getReference()获取当前AtomicStampedReference对象持有的引用值。void set(V newRef, int newStamp)设置AtomicStampedReference对象的引用值为newRef标记值戳为newStamp。boolean compareAndSet(V expectRef, V newRef, int expectStamp, int newStamp)将AtomicStampedReference对象的引用值与期望值expectRef、标记值戳与期望值expectStamp进行比较如果相等则将其更新为新值newRef和newStamp。该操作是原子性的返回是否更新成功。int attemptStamp(V expectedReference, int newStamp)如果当前引用值等于expectedReference则尝试将标记值戳更新为newStamp。如果更新成功返回新的标记值戳否则返回当前标记值。int getStamp()获取当前AtomicStampedReference对象持有的标记值戳。
案例代码
package Example2107;import java.util.concurrent.atomic.AtomicStampedReference;class Draw{private String content ;private String autor ;private String title ;Draw(String content,String autor,String title){this.content content;this.autor autor;this.title title;}public void setContent(String content) {this.content content;}public String getContent() {return content;}
}
public class javaDemo {public static void main(String[] args) {Draw draw1 new Draw(,alphaMilk,JUC并发编程原子操作类);
// 初始化内容版本号为1AtomicStampedReferenceDraw atomicDraw new AtomicStampedReferenceDraw(draw1,1);System.out.println(atomicDraw.getReference());
// 更新内容版本号更改draw1.setContent(Hello,word);atomicDraw.set(draw1,2);
// 获取当前版本System.out.println(atomicDraw.getStamp());}
}AtomicMarkableReference与AtomicStampedReference的区别在于一个是设置boolean类型的初始化标记一个多设置的是int类型版本号
下面是AtomicMarkableReference的常用方法
方法描述AtomicMarkableReference(V initRef, boolean initMark)构造一个AtomicMarkableReference对象初始引用值为initRef初始标记值为initMark。V getReference()获取当前AtomicMarkableReference对象持有的引用值。boolean isMarked()判断当前AtomicMarkableReference对象是否被标记。boolean compareAndSet(V expectRef, V newRef, boolean expectMark, boolean newMark)将AtomicMarkableReference对象的引用值与期望值expectRef、标记值与期望值expectMark进行比较如果相等则将其更新为新值newRef和newMark。该操作是原子性的返回是否更新成功。void set(V newRef, boolean newMark)设置AtomicMarkableReference对象的引用值为newRef标记值为newMark。boolean attemptMark(V expectedReference, boolean newMark)如果当前引用值等于expectedReference则尝试将标记值更新为newMark。如果更新成功返回true否则返回false。
案例代码
一个班统计同学是否交了班费
package Example2108;import java.util.concurrent.atomic.AtomicMarkableReference;class Student{private String name;private int id;Student(String name,int id){this.name name;this.id id;}
}
public class javaDemo {public static void main(String[] args) {Student stu1 new Student(王一,001);Student stu2 new Student(张二蛋,002);
// 王一交过班费AtomicMarkableReferenceStudent atoStu new AtomicMarkableReferenceStudent(stu1,true);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println(该同学交过班费);}else System.out.println(该同学尚未交过班费);
// 张二蛋没有交班费atoStu.set(stu2,false);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println(该同学交过班费);}else System.out.println(该同学尚未交过班费);}
}4.对象属性修改原子类 AtomicIntegerFieldUpdaterAtomicLongFiledUpdaterAtomicReferenceFieldUpdater
这三个类的实现原理基本差不多所以将用AtomicIntegerFieldUpdater举例
以下是AtomicIntegerFieldUpdater类的常用方法
int addAndGet(T obj, int data)将指定对象obj的字段值与data相加并返回相加后的结果。boolean compareAndSet(T obj, int expect, int update)将指定对象obj的字段值与期望值expect进行比较如果相等则将其更新为新值update。返回是否更新成功。int get(T obj)获取指定对象obj的字段值。int getAndSet(T obj, int newValue)获取指定对象obj的字段值并将其设置为新值newValue。int decrementAndGet(T obj)将指定对象obj的字段值减1并返回减1后的结果。int incrementAndGet(T obj)将指定对象obj的字段值加1并返回加1后的结果。 案例代码
package Example2109;import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;class Book{volatile long id;volatile String name;Book(long id, String name){this.id id;this.name name;}}public class javaDemo {public static void main(String[] args) {Book book1 new Book(114514,Java从入门到入土);AtomicReferenceFieldUpdaterBook,String bookmanger AtomicReferenceFieldUpdater.newUpdater(Book.class,String.class,name);System.out.println(更新前书本名称为:bookmanger.get(book1));bookmanger.set(book1,Java从入门到项目实战);System.out.println(更新后书本名称为:bookmanger.get(book1));}
}4.CAS 、AQS机制
CAS是一条CPU并发原语。它的功能是判断某个内存某个位置的值是否相等如果是则改为新的值这个操作过程属于原子性操作。
CAS是乐观锁是一种冲突重试机制在并发竞争不是很剧烈的情况下其操作性能会好于悲观锁机制Synchronization同步处理
*面试题为什么说 Synchronized 是一个悲观锁乐观锁的实现原理又是什么什么是 CAS它有什么特性 Synchronized的并发策略是悲观的不管是否产生竞争任何数据的操作都必须加锁。乐观锁的核心是CASCAS包括内存值、预期值、新值只有当内存值等于预期值时才会将内存值修改为新值。 *面试题乐观锁一定就是好的吗 乐观锁认为对一个对象的操作不会引发冲突所以每次操作都不进行加锁只是在最后提交更改时验证是否发生冲突如果冲突则再试一遍直至成功为止这个尝试的过程称为自旋。乐观锁没有加锁但乐观锁引入了ABA问题此时一般采用版本号进行控制也可能产生自旋次数过多问题此时并不能提高效率反而不如直接加锁的效率高只能保证一个对象的原子性可以封装成对象再进行CAS操作 *面试题volatile 关键字的作用 对于可见性Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happens-before 的保证确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时它会保证修改的值会立即被更新到主存当有其他线程需要读取时它会去内存中读取新值。 从实践角度而言volatile 的一个重要作用就是和 CAS 结合保证了原子性详细的可以参见 java.util.concurrent.atomic 包下的类比如 AtomicInteger。 volatile 常用于多线程环境下的单次操作(单次读或者单次写)。