GeorgeYang'Blog

my technology blog

安卓开发笔记8.23

阅读:499 创建时间:2017-08-22 22:38:40 tags:[android]

开发注意事项:

  • 集合初始化时,尽量指定集合初始值大小

ArrayList尽量使用ArrayList(int initialCapacity) 初始化 。

  • 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历

说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。

  • 高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:

集合类 | Key | Value | Super | 说明 ---|--- Hashtable | 不允许为null | 不允许为null | Dictionary | 线程安全 ConcurrentHashMap | 不允许为null | 不允许为null | AbstractMap | 分段锁技术 TreeMap | 不允许为null | 允许为null | AbstractMap | 线程不安全 HashMap | 允许为null | 允许为null | AbstractMap | 线程不安全

  • 元素去重

利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作。

参考:https://juejin.im/entry/59955f07518825243f1b3002

线程相关知识:

  • 中断线程:

平时我们使用的Thread.interrupt方法中断线程,线程仍然运行,真正中断一个线程的几种方法:

  1. 使用共享变量(shared variable)

    class Example2 extends Thread { volatile boolean stop = false; public void run() { while ( !stop ) { ..... } } }

  2. 线程阻塞的中断方法

Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。

 class Example2 extends Thread {
   public void run() {
     try {
       .....
       Thread.sleep( 1000 );
     } catch ( InterruptedException e ) {
       System.out.println( "Thread interrupted..." );
     }
   }
 }

3.中断I/O操作

如果线程被I/O操作阻塞,该线程将接收到一个SocketException异常,这与使用interrupt()方法引起一个InterruptedException异常被抛出非常相似。

 class Example2 extends Thread {
   volatile ServerSocket socket;
   public void run() {
     try {
       socket = new ServerSocket(7856);
     } catch ( IOException e ) {
      System.out.println( "Could not create the socket..." );
       return;
     }

      System.out.println( "Waiting for connection..." );
       try {
        Socket sock = socket.accept();
       } catch ( IOException e ) {
       System.out.println( "accept() failed or interrupted..." );
       }

   }
 }

参考:http://www.cnblogs.com/simonshi/archive/2011/12/31/2308455.html

  • volatile特点:

1.volatile变量的写先发生于读,这保证了volatile变量的可见性

2.volatile是线程同步的轻量级实现,主要作用是使变量在多线程间可见

3.volatile本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存里

4.volatile会强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取的变量的值

5.volatile变量自身具有三个语义特性:

可见性:保证了不同线程对这个变量进行操作时的可见性,即变量一旦变更所有线程立即可见

有限原子性:对任意单个volatile变量的简单读写操作具有原子性,复合操作不具有原子性(如i++)

重排序禁止:禁止进行指令重排序

  • volatile写操作的内存语义

当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存

  • volatile读操作的内存语义

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程会直接从主内存中读取共享变量 此时实际上是线程间通过主内存完成了一次消息通信,即线程A向B发送消息

  • volatile可见性实现原理

为了实现volatile内存语义,JMM会分别限制编译器重排序和处理器重排序

为了保证内存可见性,编译器会在生成指令序列的恰当位置插入内存屏障指令来禁止特定类型的处理器重排序

实现原理是在指令序列执行过程中,通过在volatile写操作后面插入StoreLoad屏障(x86平台),仅对volatile写-读进行重排序(x86会忽略读-读、读-写、写-写的重排序)从而实现正确的内存语义

执行该屏障开销昂贵,因为处理器通常会把写缓冲区的数据全部刷新到内存中 具体的实现原理已超出本文所限,读者有兴趣可参见 Java并发编程的艺术 的 第三章-Java内存模型

  • Synchronized vs Volatile
  • 作用: volatile解决的是变量在多线程间的可见性,而syn解决的是多线程间访问资源的同步性

  • 修饰域: volatile只能修饰变量;synchronized可以修饰方法和代码块

  • 阻塞: 多线程访问volatile不会发生阻塞;synchronized会出现阻塞

  • 原子性: volatile保证数据可见性,但不保证原子性;而syn可以保证原子性,也可以间接保证可见性,因为会将私有内存和公共内存中的数据做同步

  • 性能: volatile是线程同步的轻量级实现,性能优于synchronized,但synchronized的性能不断被优化提升,实际上表现不差

参考:https://juejin.im/entry/59956fe4518825243a78b1ce