一、JUC开发包简介
【 java.util.concurrent开发包 】11)ScheduldExecutorService:一个ExecutorService,可安排在给定的延迟后运行或定期执行的命令。
二、TimeUnit工具类
在java.util.concurrent开发包里面提供有一个TimeUnit类, 时间单元类。该类是一个枚举类型,这也是juc开发包里面唯一的一个枚举类。
public enum TimeUnit extends Enum
这个类之中支持有:日、时、分、秒、毫秒、微妙、纳秒,这些是更加精准的控制单位。
• 休眠的处理只有Thread.sleep()方法。
public static void main(String[] args) throws Exception {
System.out.println("【START】" + System.currentTimeMillis());
Thread.sleep(2 * 1000); // 需要自己根据毫秒来进行计算
System.out.println("【END】" + System.currentTimeMillis());
}
那么这个时候对于以上的计算可以直接利用TimeUnit做准确处理。
范例:实现转换处理
public long convert(long sourceDuration, TimeUnit sourceUnit) {
throw new AbstractMethodError();
}
———————
public static void main(String[] args) throws Exception {
long time = TimeUnit.MILLISECONDS
.convert(1, TimeUnit.HOURS) ;
System.out.println("一小时转为毫秒:" + time);
}
一小时转为毫秒:3600000
范例:想知道3天后的日期
public static void main(String[] args) throws Exception {
long time = TimeUnit.MILLISECONDS.convert(3, TimeUnit.DAYS);
System.out.println("3天转为毫秒:" + time);
// 当前时间的毫秒数 + 三天后的毫秒数
long threeTime = System.currentTimeMillis() + time;
System.out.println("3天后的日期:" + new Date(threeTime));
System.out.println("3天后的日期:" + new SimpleDateFormat(
"yyyy-MM-dd").format(new Date(threeTime)));
}
3天转为毫秒:259200000
如果不使用这样的处理操作,则这个时间间隔的计算将会非常麻烦,需要各种乘法计算。
三、原子操作类
既然强调了并发访问,那么就必须考虑操作系统位数;32位操作系统还是64位操作系统,对于Long数据类型而言,是64位的,如果现在项目运行在32位上,则long这个数据类型会占据两个32位空间进行数据的保存。
如果现在每一个程序类里面都去使用long进行处理的时候都手工进行volatile配置是不是太麻烦了,为了解决这样的问题,在juc里面提供有一个atomic子包,这个自包里面保存的都是原子性的操作数据,也就是说这个包里所包含的数据类型前都使用volatile进行声明。
【 原子操作分类 】
➣ 原子操作,是指操作过程不会被中断,保证数据操作是以原子方式进行的;
• 基本类型:AtomicInteger, AtomicLong, AtomicBoolean.
• 数组类型:AtomicIntegerArray, AtomicLongArray.
• 引用类型:AtomicReference, AtomicStampedRerence.
• 对象的属性修改类型:AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater.
AtomicBoolean AtomicReference
AtomicInteger AtomicReferenceArray
AtomicIntegerArray AtomicReferenceFieldUpdater
AtomicIntegerFieldUpdater AtomicStampedReference
AtomicLong DoubleAccumulator
AtomicLongArray DoubleAdder
AtomicLongFieldUpdater LongAccumulator
AtomicMarkableReference LongAdder
范例:观察“AtomicLong”类型
实际上使用 AtomicLong 的时候里面包含有的就是”private volatile long value;”
public static void main(String[] args) throws Exception {
AtomicLong num = new AtomicLong(100) ; // 设置为原子性操作
System.out.println("数据自增:" + num.incrementAndGet()); // 101
System.out.println("数减自增:" + num.decrementAndGet()); // 100
}
范例:实现基础数学运算
}
毕竟这种操作不是原始的基本数据类型,它的操作时刻需要保证数据在多线程访问下的并发安全性,对于原子性的处理操作,以上并不是它的重点,只是它的操作形式,这里面最为重要的一个方法:
CAS方法:public final boolean compareAndSet(long expect, long update);
范例:观察CAS的方法使用
public static void main(String[] args) throws Exception {
AtomicLong num = new AtomicLong(100) ; // 设置为原子性操作
// 如果现在要进行修改的内容是100,即:原始的原子类型里面为100,则使用333替换num的内容
// 比较的值等于100,返回true
System.out.println(num.compareAndSet(100, 333));
System.out.println(num); // 结果:333
}
使用CAS方法进行内容修改的时候一定要设置一个原始的比较内容,如果内容相同才可以修改,如果现在操作的是数组也有与之对应的程序类:
范例:进行数组操作
public static void main(String[] args) throws Exception {
AtomicLongArray array = new AtomicLongArray(new long[]{1, 2, 3});
array.set(0, 99); // 原子性的数组必须使用set修改内容
System.out.println(array); // 输出结果:[99, 2, 3]
}
范例:使用原子性进行对象的描述
import java.util.concurrent.atomic.AtomicReference;
public class MLDNTestDemo {
public static void main(String[] args) throws Exception {
AtomicReference ref = new AtomicReference();
Member memA = new Member("张三", 20);
Member memB = new Member("李四", 30);
ref.set(memA);
// 对象引用变更只得依靠地址比较“==”
ref.compareAndSet(memA, memB);
System.out.println(ref); // 结果:name = 李四、age = 30
}
}
class Member {
private String name;
private int age;
public Member(String name, int age) {
this.name = name; this.age = age;
}
@Override
public String toString() {
return "name = " + this.name + "、age = " + this.age;
}
}
以上集中类型严格来讲都算是常用的几种处理形式,但有一个问题,即:你可能本身类中定义的类型不是AtomicLong类型,那么可以用AtomicLongFieldUpdater类完成处理。
获得对象: public static AtomicLongFieldUpdater newUpdater(Class tclass, String fieldName);
范例:使用AtomicLongFieldUpdater更新器
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public class MLDNTestDemo {
public static void main(String[] args) throws Exception {
Book book = new Book(100000001,"Java开发实战") ;
book.setBid(200000003); // 修改bid
System.out.println(book);
}
}
class Book {
private volatile long bid ; // 必须追加volatile关键字
private String title ;
public Book(long bid,String title) {
this.bid = bid ;
this.title = title ;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setBid(long bid) {
AtomicLongFieldUpdater updater = AtomicLongFieldUpdater.newUpdater(
super.getClass(), "bid") ;
// 使用cas方法进行内容的修改
updater.compareAndSet(this, this.bid, bid) ;
}
@Override
public String toString() {
return "图书编号:" + this.bid + "、名称:" + this.title;
}
} //输出结果:图书编号:200000003、名称:Java开发实战
四、ThreadFactory线程工厂类
}
范例:使用ThreadFactory程序类
import java.util.concurrent.ThreadFactory;
class SimpleThreadFactory implements ThreadFactory {
private static int count = 0 ; // 进行一个计数的操作
public Thread newThread(Runnable r) {
return new Thread(r, "线程 - " + count++);
}
}
public class MLDNTestDemo {
public static void main(String[] args) throws Exception {
Thread t = new SimpleThreadFactory().newThread(()->{
for (int x = 0 ; x < 10 ; x ++) {
System.out.println(Thread.currentThread().getName()
+ "、x = " + x);
}
}) ;
t.start();
}
}
以上输出的结果:
线程 - 0、x = 0
线程 - 0、x = 1
线程 - 0、x = 2
线程 - 0、x = 3
线程 - 0、x = 4
线程 - 0、x = 5
线程 - 0、x = 6
线程 - 0、x = 7
线程 - 0、x = 8
线程 - 0、x = 9
实际上对于接口对象一直强调:必须通过工厂类来获得实例化对象,所以ThreadFactory设计虽然看起来多余,但是复合标准话的设计,可是很多的开发者未必会这样去操作,在以后进行一些程序的内部结构分析上还是需要使用ThreadFactory。