/images/avatar.png

Java开发手册-(转自阿里云栖社区)

Java开发手册下载链接

1-0.9=0.1是天经地义的,但在计算机的世界里,0.1恰恰是无法精确表示的一个小数,只有2的幂次倍小数才能够精确表示,如:0.5、0.25、0.125等。由于0.1是近似表达,在各种情形中的计算存在数位的取舍精度不一样,所以1-0.9未必等于0.9-0.8,所以浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。包装类型运算也是调用的浮点类型的计算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class FloatPrimitiveTest {
    public static void main(String[] args) {
        float a = 1.0f - 0.9f;
        float b = 0.9f - 0.8f;
        if (a == b) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }
    }
}
输出------------false


public class FloatWrapperTest {
    public static void main(String[] args) {
        Float a = Float.valueOf(1.0f - 0.9f);
        Float b = Float.valueOf(0.9f - 0.8f);
        if (a.equals(b)) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }
    }
}
输出--------false
正例--------------------------------
(1) 指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。
 float a = 1.0f - 0.9f;
 float b = 0.9f - 0.8f;
 float diff = 1e-6f;
 if (Math.abs(a - b) < diff) {
     System.out.println("true");
 }
(2) 使用 BigDecimal 来定义值,再进行浮点数的运算操作。
 BigDecimal a = new BigDecimal("1.0");
 BigDecimal b = new BigDecimal("0.9");
 BigDecimal c = new BigDecimal("0.8");
 BigDecimal x = a.subtract(b);
 BigDecimal y = b.subtract(c);
 if (x.equals(y)) {
     System.out.println("true");
 }

并发编程学习(4)Lock

初步认识JUC

Java.util.concurrent 是在并发编程中比较常用的工具类,里面包含很多用来在并发场景中使用的组件。比如线程池、阻塞队列、计时器、同步器、并发集合等等。并发包的作者是大名鼎鼎的 Doug Lea。

lock

在 Lock 接口出现之前,Java 中的应用程序对于多线程的并发安全处理只能基于synchronized 关键字来解决。但是 synchronized 在有些场景中会存在一些短板,也就是它并不适合于所有的并发场景。但是在 Java5 以后,Lock 的出现可以解决synchronized 在某些场景中的短板,它比 synchronized 更加灵活。 Lock 本质上是一个接口,它定义了释放锁和获得锁的抽象方法,定义成接口就意味着它定义了锁的一个标准规范,也同时意味着锁的不同实现。实现 Lock 接口的类有很多,以下为几个常见的锁实现

Lock接口

1
2
3
4
5
void lock() // 如果锁可用就获得锁,如果锁不可用就阻塞直到锁释放
void lockInterruptibly() // 和lock()方法相似, 但阻塞的线程可中断,抛 出java.lang.InterruptedException 异常
boolean tryLock() // 非阻塞获取锁;尝试获取锁,如果成功返回 true
boolean tryLock(longtimeout, TimeUnit timeUnit)//带有超时时间的获取锁方法
void unlock() // 释放锁

类图 https://yakax.oss-cn-hangzhou.aliyuncs.com/blog/Concurrent/4/6.png

并发编程学习(3)线程安全性分析

初步认识 Volatile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    public /*volatile*/ static boolean stop=false;
    public static void main( String[] args ) throws InterruptedException {
        Thread t1=new Thread(()->{
            int i=0;
            while(!stop){ 
                i++;
            }
        });
        t1.start();
        Thread.sleep(1000);
        stop=true; //true
    }
  • 定义一个共享变量 stop
  • 在main线程中创建一个子线程 thread,子线程读取到 stop的值做循环结束的条件
  • main线程中修改stop的值为 true
  • 当 stop没有增加volatile修饰时,子线程对于主线程的 stop=true的修改是不可见的,这样将导致子线程出现死循环
  • 当 stop增加了volatile修饰时,子线程可以获取到主线程对于 stop=true的值,子线程while循环条件不满足退出循环

增加volatile关键字以后,main线程对于共享变量 stop值的更新,对于子线程 thread可见,这就是volatile的作用

并发编程学习(2)synchronized与锁的唤醒

synchronized 的基本认识

在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁。但是,随着 Java SE 1.6 对synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁。

synchronized 的基本语法

synchronized 有三种方式来加锁,分别是

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class SyncDemo {
    Object lock = new Object();

    public synchronized void demo1() {

    }

    public void demo2() {
        // TODO
        synchronized (this) {

        }
        // TODO
    }

    public synchronized static void demo3() {

    }

    public void demo4() {
        synchronized (SyncDemo.class) {

        }
    }

    public void demo5() {
        synchronized (lock) {

        }
    }
}

不同的修饰类型,代表锁的控制粒度