一个连胡说八道都说不好的程序员.

  • Java并发实现之关键字Synchronized


    作用

    在Java中,关键字 synchronized 用来修饰方法,也能以块的方式来使用。主要的作用时确保多个线程在同一时刻最多只能有一个线程处于方法或者块中。

    锁的对象

    在Java中,每个对象都可以作为锁,当用 synchronized 来实现同步的时候,根据不同情况 synchronized 具体表现有所不同:

    • 对于synchronized修饰的普通方法,锁的是当前实例对象,相当于 this
    • 对于synchronized修饰的静态方法,锁的是当前类的Class对象。
    • 对于synchronized修饰的块,锁的是显示声明的锁的对象,语法为 synchronized(obj){ }

    JVM对于synchronized的优化处理

    Java中锁的状态

    为了减少锁获取和释放带来的性能消耗,从Java SE 6开始,引入了新的 偏向锁 和 轻量级锁 的概念。

    锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(锁的升级是单向的,不会出现锁的降级)。

    偏向锁

    在大多数情况下,锁其实不存在多线程的竞争的,而且总是由同一线程多次获取,所以为了让线程后去的成本更低,JVM中引入了偏向锁。

    偏向锁主要解决无竞争下的锁性能问题,偏向锁的原理是一旦线程第一次获得了锁,会在锁的对象头和栈帧中的锁纪录里存储线程的ID,以后当该线程再次获取和释放锁的时候不需要进行CAS操作,只需要测试锁的对象头里面是否存在指向当前线程的偏向锁,如果成功,表示线程已经获取了锁。 如果失败,需要测试锁对象中锁的标志位(锁的标志位定义如表1):

    • 如果锁标示为1,将会尝试通过CAS讲对象头中偏向锁指向当前线程。
    • 如果不为1,使用CAS竞争锁。
    轻量级锁 00
    偏向锁 01
    重量级锁 10

    表1: Java对象中锁标志位的意义

    只有当其他线程尝试竞争偏向锁时,持有了偏向锁的线程才会释放锁。偏向锁的释放,会暂停拥有偏向锁的线程,然后测试持有偏向锁的线程是否存活,如果线程处于不活动状态,则将对象头设置为无锁状态;反之如果线程处于活动状态,将会重新偏向于其他线程或者恢复到无锁状态或者升级为轻量级锁。

    JDK 1.6中默认是开启偏向锁和轻量级锁的,我们也可以通过-XX:-UseBiasedLocking来禁用偏向锁, 也可以使用 -XX:BiasedLockingStartupDelay=0 来关闭偏向锁的激活延迟。

    轻量级锁

    “轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

    轻量级锁的加锁

    1. 如果锁对象状态为无锁状(锁标志位为01,是否为偏向锁为0),JVM首先将在当前线程的栈帧中建立一个名为锁记录的空间,称之为 Displaced Mark Word
    2. 将对象头中的Mark Word复制到锁纪录中。
    3. 线程尝试通过CAS将对象头中的Mark Word设置为指向锁纪录,如果成功,标示当前线程获得锁;反之如果失败,意味着有其他线程在竞争锁,当前线程将会通过自旋的方式来获取锁。

    轻量级锁的释放

    1. 通过CAS操作尝试把Displaced Mark Word替换回对象头。
    2. 如果替换成功,意味着没竞争发生。
    3. 如果替换失败,说明有其他线程尝试过获取该锁(此时锁已膨胀),在释放锁的同时,唤醒被挂起的线程。

  • Java并发实现之关键字Volatile


    作用

    在Java中,关键字 volatile 用来修饰类的成员变量,意味着任何对该变量的访问都需要从共享内存中读取,而非从本地私有内存(Local Memory)中读取。且对变量值的改变也必须同步写回到共享内存。在Java并发中,volatile 关键字能保证所有线程对访问变量的可见性。

    解释

    在Java中,线程之间的通信是通过共享内存来实现的(线程之间共享堆内存,通过读写来进行通信)。所有的线程都有自己的本地私有内存(Local Memory),本地私有内存存放有该线程需要读写的共享变量的副本。因为这个副本的存在,不同线程之间读取同一个共享变量的值有可能是已经过期了的。 在读写一个Volatile修饰的变量时候,按照下面的方法进行处理:

    1. 当更新Volatile变量时候,会把对应本地内存中共享变量值写回住内存中。
    2. 当读取Volatile变量时候,将会标记本地内存中值无效,然后从主内存中读取对应的共享变量的值。

    正确使用

    Volatile 变量具有可见性,但是不具备原子特性,要使 volatile 变量提供理想的线程安全,需要同时满足下面两个条件: 来自《正确使用 Volatile 变量》

    1. 对变量的写操作不依赖于当前值。
    2. 该变量没有包含在具有其他变量的不变式中。

    所以只是实用Volatile来修饰变量想要实现计数器,这是不安全的。因为我们对一个变量进行x+=1或者x++操作的时候,实际上这是一个读取值,修改值,最后写入三个序列操作组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。

    对指令重排的影响

    volatile语义保证了volatile变量在一些情况下不会重排序,volatile的64位变量double和long的读取和赋值操作都是原子的。下表为 volatile重排序规则。

    能否重排序 第二操作
    第一操作 普通读写 volatile读 volatile写
    普通读写 NO
    volatile读 NO NO NO
    volatile写 NO

  • 让Xcode在Archive的时候自动增加Target的Build号


    注: 关于xcode中target的Build作用还有其和Version的关系和区别,如果不明白的,可以看下官方的文档:Version Numbers and Build Numbers

    那么, 如果要让Build Numbers能自动增长呢?

    首先,在项目目录下新建个bash文件,我这里取名为 buildseqence.sh, 代码如下

    #!/bin/sh
    
    #  buildseqence.sh
    #  Aircraft
    #
    #  Created by cluries on 12/8/10.
    #  Copyright © 2012年 cluries. All rights reserved.
    
    if [ $# -ne 1 ]; then
        echo usage: $0 plist-file
        exit 1
    fi
    
    plist="$1"
    dir="$(dirname "$plist")"
    
    if [ "Debug" = ${CONFIGURATION} ]; then
        echo "Not incrementing build number as Debug model."
        exit 0
    fi
    
    
    buildnum=$(/usr/libexec/Plistbuddy -c "Print CFBundleVersion" "$plist")
    if [ -z "$buildnum" ]; then
        echo "No build number in $plist"
        exit 2
    fi
    
    buildnum=$(expr $buildnum + 1)
    /usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "$plist"
    echo "Incremented build number to $buildnum"
    
    

    然后进入在Target中的Build Phases添加Run Script:

    ${PROJECT_DIR}/Aircraft/buildseqence.sh "${PROJECT_DIR}/${INFOPLIST_FILE}"
    

    这样,在每次Xcode Archive操作的时候,都可以自动增加Build Number, 如果想要在Debug的时候也能自动增加,去掉Bash脚本中的下面代码就可以了:

    if [ "Debug" = ${CONFIGURATION} ]; then
        echo "Not incrementing build number as Debug model."
        exit 0
    fi
    

  • 解决通过Cocoapods安装或升级Realm时候CURL报SSLRead()的错误


    今天Realm提醒我有新版本可用。但是在pod update中报错了,如下:

     > Running prepare command
       $ /usr/local/bin/bash -c  set -e sh build.sh cocoapods-setup
       core is not a symlink. Deleting...
       Downloading dependency: core 0.100.0
       Downloading core failed:
       curl: (56) SSLRead() return error -9806
    [!] /usr/local/bin/bash -c 
    set -e
    sh build.sh cocoapods-setup
    
    core is not a symlink. Deleting...
    Downloading dependency: core 0.100.0
    Downloading core failed:
    curl: (56) SSLRead() return error -9806
    

  • RVM安装ruby 1.9.3时候错误


    因为动态库位置的变动,之前编译的ruby 1.9.3版本运行不起了,然后不想 ln -s 一下,需要重新编译ruby 1.9.3。

    rvm reinstall 1.9.3 后在编译过程中报错 :

    regparse.c:582:15: error: implicit conversion loses integer precision: 'st_index_t' (aka 'unsigned long') to 'int' [-Werror,-Wshorten-64-to-32]
        return t->num_entries;
    

    ** -Werror,-Wshorten-64-to-32 ** 编译参数和错误提示倒是很明确,不过因为是通过rvm安装的,又不能直接去修改Makefile。

    Github上解决方法是 CC=gcc rvm install 1.9.3

    这样也可以,不过适合于xcode5之前的版本,如果安装的是xcode5,因为xcode5里并没有包含llvm-gcc,所以这样是不行的。

    在安装了xcode5情况下,gcc还是clang,所以需要手动安装gcc4.2.

    通过homebrew可以快速安装: brew install apple-gcc42

    然后CC=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/gcc-4.2 rvm install 1.9.3 后就等着rvm帮忙安装1.9.3了。