月度归档:2015年11月

Java并发编程的特性

1. Java内存模型:

java内存模型

       java的对象都是在主内存(物理内存)中创建的;而各个线程在执行的时候,为了隔离相互之间的影响,以及降低CPU和内存IO之间速率相差太大的影响,会将主内存中的变量读取到工作内存中(高速缓存,寄存器),然后在工作内存中操作,完成之后再写回到主内存中。
       主内存和工作内存中的操作由以下几部分操作组成,每部分均是原子性的:
内存操作

       首先通过read、load将变量的值放入工作内存中;当执行引擎需要使用变量时,使用use操作,读取工作内存的变量,操作完之后使用assign,将新的变量值写回工作内存;当需要将变量值同步回主内存时,通过store、write操作将工作内存的值刷新到主内存中。
       java虚拟机规定read和load,store和write必须成对出现。
       每次lock一个变量,将会清空该变量在工作内存中的值。

2. 并发编程的3个特性

2.1 原子性:

  • 基本变量的读取和赋值是原子性的,结合上面的操作顺序,即可得知,因为每次read和write均是原子性的。但是java对于64位的变量,允许拆分成两部分的32位分别操作,因此对于double和long的读写,可能没有原子性。但实际上大多数jvm在具体实现时,也都将这两种类型的读写实现为原子性了。
  • synchronized。必须先lock才能进入同步代码块,因此一个线程在unlock之前,其他线程无法进入同步代码块,保证了同步代码块的原子性。

2.2 可见性

  • volatile。被volatile修饰的变量,在每次use之前,都需要先read和load,因此保证了每次读取的都是主内存的最新值;而在每次assign的时候,都需要store和write回主内存,因此保证了最新值可以刷新到主内存。
  • synchronized。每次进入同步代码块之前,都会先lock住变量,然后清空工作内存中共享变量的值;每次退出同步代码块之前,都会先将工作内存中的共享变量刷新到主内存中,然后unlock(关于synchronized的可见性,可以见:synchronized可见性)。

2.3 有序性

  • volatile。JVM规定,在volatile之前的操作,不能重排序到volatile之后。
  • synchronized。同步代码块对于不同线程来说串行进入的。

3. 一段同步代码的分析

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class ThreadTest extends Thread {
 private static boolean stop;
 // private static volatile boolean stop;

 synchronized void f() {}

 @Override
 public void run() {
  System.out.println("start");
  while (!stop) {
   // f();
   // System.out.println("loop");
  }
  System.out.println("end");
 }

 public static void main(String[] args) {
  try {
   ThreadTest t = new ThreadTest();
   t.start();
   Thread.sleep(2000);
   t.stop = true;
   System.out.println("stop");
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   System.out.println(e.getMessage());
  }
 }
}

       由于stop变量的不可见性的,所以线程并不会在stop设为true之后停止,原因是stop不具有可见性,但是如果用volatile修饰stop,或者在循环中调用synchronized方法,则可以使线程停下来;另外循环中调用控制台输出,也可以停下线程,猜测是输出到控制台的时候调用了synchronized方法。
       这段类似的代码在《Effective Java》中提到,编译器在优化的时候也可能优化为:

while(!stop){
    while(true){
        // do something
    }
}

4. 双重锁单例模式的一个细节分析

private static volatile TestSingleton instance = null;  

    public static TestSingleton getInstance() {  
           if (instance == null) {    
             synchronized (TestSingleton.class) {    
                if (singleton == null) {    
                   singleton = new TestSingleton();   
                }    
             }    
           }   
           return instance;  
    }  

       上面是一个典型的单例实现方式,关于volatile的作用,大部分地方会解释为可见性,但是此处使用synchronized已经保证了singleton的可见性,为何还要使用volatile。其实这里更多的是考虑有序性,防止代码重排序,如果在创建TestSingleton的时候,先将TestSingleton对象的内存地址返回给singleton了,而TestSingleton还没有完成初始化,此时,其他线程可能拿到一个不完整的singleton。具体可见:单例模式的实现,在这篇博文的评论中有说到。

参考资料:

  • 《深入理解Java虚拟机》
  • 《Effective Java》
  • http://blog.csdn.net/jason0539/article/details/23297037
  • http://www.imooc.com/video/6780

C++字符串转码处理

公司项目要做一个桌面应用,需要使用nodejs V8编写C++ addon(扩展)。
在C++的方法中获取入参中含有中文时,使用V8 String类下的Utf8Value类,可以构造出UTF-8编码的字符串,代码如下:

String::Utf8Value param1(args[0]->ToString());

String::Utf8Value重载了operator,返回char 类型,所以可以用

std::string str = string(*param1)

这样的方法来创建一个c++的string对象。
这样对于英文来说,是没有问题,但是对于中文,会出现乱码的情况。
一开始搜索资料时,只是将关键字设定为“char*转string 中文乱码”,能找到一些方法,多数如
http://www.blogbus.com/hx1987-logs/211009970.html
这个blog里面说的,但是都不凑效。(不过也不是没有收获,至少还了解到MultiByteToWideChar这个方法和wchar_t这个类型,C++字符转码都是需要MultiByteToWideChar来转换的)
其实这样搜出来的结果,基本上都有一个共同的问题,就是没有说明这个方法是由什么编码转成什么编码。
因为我入参是UTF8编码,而C++的中文编码,是unicode或GBK,所以查找的方向应该是如果将UTF8编码转换成GBK或unicode编码。
分享两篇博文:
关于C++中文字符的处理
VC URLEncode & UrlDecode (这篇的的话是从另外一个方向找到的:我的一个同事说,应为是用node调用,可以在js中先用encodeURI编码后再调用addon,所以我就往这个方向的搜索了一下,但是最后发现,其实在js中用decodeURI时,也是用UTF8来URIencode,所以在C++中使用URIdecode后,还是UTF8编码,不过里面的Utf8ToStringT方法最终帮我解决了问题^-^)

CString Utf8ToStringT(LPSTR str)
{
    _ASSERT(str);
    USES_CONVERSION;
    WCHAR *buf;
    int length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    buf = new WCHAR[length+1];
    ZeroMemory(buf, (length+1) * sizeof(WCHAR));
    MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, length);

    return (CString(W2T(buf)));
}

能得到正确编码的CString,然后就好办了:
CString,string,char*之间的转换(转)

最后在C++中处理完后,还需要返回中文提示信息,所以需要将GBK转成UTF8~~~~下面的一篇问题贴帮到了我:
http://bbs.csdn.net/topics/320078111
GBK转UTF8

void ConvertGBKToUtf8( CString& strGBK )
{
    int len=MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, NULL,0);
    WCHAR * wszUtf8 = new WCHAR[len+1];
    memset(wszUtf8, 0, len * 2 + 2);
    MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, wszUtf8, len);

    len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
    char *szUtf8=new char[len + 1];
    memset(szUtf8, 0, len + 1);
    WideCharToMultiByte (CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL,NULL);

    strGBK = szUtf8;
    delete[] szUtf8;
    delete[] wszUtf8;
}