|
我對 Java 關鍵字 Synchronized 的新理解 |
My New Understanding of Java's Synchronized Keyword |
說實話,我對 Java 并發(fā)編程知之不多。我曾經常用關鍵字 volatile 試圖“強制原子操作”,結果帶來的麻煩比解決的還多。Sun Java 教程中的并發(fā)課程我以前從沒看完過,現在該通讀一遍了。 |
To be honest, I knew only a little about concurrent programming in Java. I uesed to use keyword volatile as an attempt to "enforce atomic operations" which had brought me more troubles than solved. Time to walk through the Concurrency Trail of Sun's Java Tutorials that I never finished reading in the past. |
我其實知道并經常看到關鍵字 synchronized 的使用,但直到昨天我還沒發(fā)覺就這個字消除了很多同步問題。然而,真正的答案在我第一次看這個教程時就在里面了,到這次才弄清。 |
I do know and often see the usage of keyword synchronized, but until yesterday I hadn't figured out how thie single word elimated so many synchronization problems. However, the very answer lies in those tutorials ever since I first read it and this time it has been clearly understood. |
每個對象都關聯(lián)有一個內部鎖,也被稱作監(jiān)視器鎖或簡稱監(jiān)視器。當一個線程調用一個同步方法時,它自動請求此方法的內部鎖,并在方法返回時釋放。即使是未捕獲的異常造成了返回,也會發(fā)生鎖的釋放。而對靜態(tài)同步方法,方法所在類的 Class 對象的內部鎖被請求。同步語句的內部行為沒什么兩樣,只是還需要顯示指定一個需要請求其內部鎖的任意對象。 |
Every boject has an intrinsic lock, which is also known as monitor lock or monitor for short, associated with it. When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception. As for a static synchronized method, an intrinsic lock for the Class object of that method's Class is acquired instead. Synchronized statements internally behaves no differently except in addition to this, an arbitrary object whose intrinsic lock will be acquired can be and should be explicitly specified.
|
總之,synchronized 關鍵字是鎖定對象的簡單方式,也有很多局限。java.util.concurrency.locks 包支持更高深的鎖定用法,也是我將要學的。 |
In conclusion, synchronized keyword is a simplified way of locking objects, and also has many limitations. More sophisticated locking idioms are supported by the java.util.concurrency.locks package which I am going to learn.
|
用 Java Sound API 播放 PCM 音頻 |
Playing PCM Audio with Java Sound API |
多媒體應用程序,比如因特網電話,需要實時播放音頻,于是就要處理內存中的音頻數據。本文將演示如何播放 PCM(脈沖編碼調制)音頻。 |
Multimedia application, such as an Internet phone, requires playing sound in real time. Thus we are going to deal with in-memery audio data. This article will show how to play PCM (Pulse Code Modulation) audio. |
假設有一個字節(jié)數組 audioBuffer 用來保存將要播放的音頻塊。做以下的初始化: |
Suppose there is a byte array audioBuffer which holds a chunck of audio data to be played. Do the following initializations: |
- AudioFormat af = new AudioFormat(
- sampleRate, sampleSizeInBits, channels, signed, bigEndian);
- SourceDataLine.Info info = new DataLine.Info(
- SourceDataLine.class, af, bufferSize);
- SourceDataLine sdl = (SourceDataLine) AudioSystem.getLine(info);
- sdl.open(af);
- sdl.start();
|
接著只要不停地用 sdl 向混頻器寫入字節(jié): |
Then just continously write bytes to mixer with sdl: |
- int offset = 0;
- while (offset < audioData.length) {
- offset += sdl.write(audioData, offset, bufferSize);
- }
|
因為 audioBuffer 被不斷填充和播放,所以最好把上述代碼放入線程。播放結束后像這樣來停止 sdl: |
Since audioBuffer is filled and played on and on, it's best to put the above code in a thread. When the playing is over, stop sdl like this: |
|
用 NetBeans 開發(fā)一個簡單的 Windows XP 程序 - 其三 |
Developing A simple Windows XP Application with NetBeans - Part 3 |
新建一個名為 MyMakefile.mk 的 Makefile,內容如下: |
Create a new Makefile named MyMakefile.mk of the following content: |
- PRJ=dist/Release/WinHello.exe
- OBJ=build/Release/WinHello.o build/Release/Resource.o
- CC=gcc
- CFLAGS=-mwindows -s
- RES=windres
- LIB=-lcomctl32
-
- $(PRJ): $(OBJ)
- mkdir -p dist/Release
- $(CC) $(CFLAGS) -o dist/Release/WinHello $(OBJ) $(LIB)
-
- build/Release/WinHello.o: WinHello.c
- mkdir -p build/Release
- $(CC) $(CFLAGS) -c -O4 -o build/Release/WinHello.o WinHello.c
-
- build/Release/Resource.o: Resource.rc
- mkdir -p build/Release
- $(RES) -o build/Release/Resource.o Resource.rc
-
- .PHONY: clean
- clean:
- ${RM} $(OBJ) $(PRJ)
|
注意由于排版問題,制表符被擴展成了空格。把這個 Makefile 添加進項目,在上面右鍵選擇“make”,就能得到如下的預期效果了: |
Note that because of typesetting, tabs are expanded to spaces. Add this Makefile to the project, right click and select "make", and the following result is shown as expected: |
 |
用 NetBeans 開發(fā)一個簡單的 Windows XP 程序 - 其二 |
Developing A simple Windows XP Application with NetBeans - Part 2 |
在項目節(jié)點上右鍵轉到“屬性 → C/C++ → C 編譯器”節(jié)點,在“命令行”中設置“其他選項”為 -mwindows;再轉到“鏈接器 → 庫”,單擊“庫”右邊的省略號按鈕,接著單擊“添加選項”,設置“其他選項”為 -lcomctl32。 |
Right click the project node and go to "Properties → C/C++ → C Compiler" node, in "Command Line" set "Additonal Options" to -mwindows; then go to "Linker → Libraries", click the ellipsis button to the right of "Libraries", click "Add Option" and set "Other Option" to -lcomctl32. |
這時如果直接執(zhí)行“生成項目”,則會輸出以下錯誤: |
If we directly execute "Build Project" right now, the following errors will be complained: |
- WinHello.c:6: error: `INITCOMMONCONTROLSEX' undeclared (first use in this function)
- WinHello.c:6: error: (Each undeclared identifier is reported only once
- WinHello.c:6: error: for each function it appears in.)
- WinHello.c:6: error: parse error before "init"
- WinHello.c:7: error: `init' undeclared (first use in this function)
- WinHello.c:8: error: `ICC_STANDARD_CLASSES' undeclared (first use in this function)
|
結構體 INITCOMMONCONTROLSEX 在 commctrl.h 中聲明,commctrl.h 位于 mingw\include。打開這個頭文件,查找 INITCOMMONCONTROLSEX,發(fā)現它包含在一段預處理指令中: |
Constuct INITCOMMONCONTROLSEX is decleared in commctrl.h, which is inside mingw\include. Open this header file and search INITCOMMONCONTROLSEX, and the following segment of preprocessed commands will be found: |
- #if
(_WIN32_IE >= 0x0300)
typedef struct tagINITCOMMONCONTROLSEX {
DWORD dwSize;
DWORD dwICC;
} INITCOMMONCONTROLSEX,*LPINITCOMMONCONTROLSEX;
#endif
|
再查找 _WIN32_IE,找到: |
Then search _WIN32_IE and find: |
- #if
0
#define _WIN32_IE 0x0300
#endif
|
#if 0 表示在默認情況下,永遠不會定義 _WIN32_IE。注釋掉行 1 和行 3,再編譯,仍然出錯: |
#if 0 means _WIN32_IE would never be definded by default. Comment line 1 and line 3, compile and still gets errors: |
- WinHello.c:8: error: `ICC_STANDARD_CLASSES' undeclared (first use in this function)
- WinHello.c:8: error: (Each undeclared identifier is reported only once
- WinHello.c:8: error: for each function it appears in.)
|
像上面一樣去查找 ICC_STANDARD_CLASSES,這次要把 windef.h 中的 WINVER 改為 0x0501。我覺得不必擔心改了這些會帶來什么壞處。這些變量存在的意義是定義或不定義一些 API,以便為不同版本的 Windows 開發(fā)程序。如今 Windows Vista 已經出來了,世界進步很快,有新東西為什么不用呢? |
Search ICC_STANDARD_CLASSES as above, and this time modify WINVER in windef.h to 0x0501. I don't think it necessary to warry that these modifications do any thing bad. The purpose of the existance of these variables is to define some APLs or not, for the sake of developing applications for various versions of Windows. Windows Vista has been present, and the world is rapidly progressing, so why not use new things? |
現在編譯運行,不會出錯了,但風格是 9x/2000 那種老土的,這是因為默認情況下 NetBeans 不知道如何編譯 rc 文件。解決這一問題的辦法是自定義一個 Makefile。 |
Compile, run, and no error now, but the style is ugly as 9x/2000's, that's because by default NetBeans dosen't know how to compile rc files. The solution to this peoblem is to write a custom Makefile. |
好像差不多長了,下次繼續(xù)。 |
This seems long enough and is to be continued next time. |
用 NetBeans 開發(fā)一個簡單的 Windows XP 程序 - 其一 |
Developing A simple Windows XP Application with NetBeans - Part 1 |
首先創(chuàng)建一個名為 WinHello 的項目,在“源代碼”節(jié)點下新建 WinHello.c,代碼的內容如下: |
First create a project with the name WinHello, and then create a new WinHello.c under "Source Files" node with the following code: |
- #include <windows.h>
- #include <commctrl.h>
-
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
- PSTR szCmdLine, int iCmdShow) {
- INITCOMMONCONTROLSEX init;
- init.dwSize = sizeof(init);
- init.dwICC = ICC_STANDARD_CLASSES;
- InitCommonControlsEx(&init);
- MessageBox(NULL, TEXT("再來!"), TEXT("哈哈~"), 0);
- return 0;
- }
|
注意末尾處最好再加上一個回車符,因為我們將用 MinGW GCC 來編譯,遵循 UNIX 的規(guī)矩總是好的。行 6~9 指明用 Windows XP 風格初始化程序,但這還不夠,我們還需要一個資源腳本和一個清單文件來顯示調用 Comctl32.dll 版本 6(默認狀態(tài)下自動調用版本 5,也就是 Windows 9x/2000 風格)。在“資源文件”節(jié)點下新建資源腳本 resource.rc 和清單文件 WinHello.exe.manifest。resource.rc 的內容如下: |
Attention it's best to add a CR in the end, because we'll compile it with MinGW GCC, so it's always good to follow the UNIX conventions. Line 6~9 indicates Windows XP style will be used to initialize the application, but that's not enough. We still need a resource script and a manifest file to explicitly invoke Comctl32.dll version 6 (version 5 is automatically invoked by default which is Windows 9x/2000 style). Create a new resource script resource.rc and a manifest file WinHello.exe.manifest. The content of resource.rc is as below: |
- #include <windows.h>
-
- CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "Winhello.exe.manifest"
|
WinHello.exe.manifest 的內容如下: |
The content of WinHello.exe.manifest is as below: |
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
- <assemblyIdentity
- version="1.0.0.0"
- processorArchitecture="X86"
- name="zhyi.zhyi.Winhello"
- type="win32"
- />
- <description>Description.</description>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity
- type="win32"
- name="Microsoft.Windows.Common-Controls"
- version="6.0.0.0"
- processorArchitecture="X86"
- publicKeyToken="6595b64144ccf1df"
- language="*"
- />
- </dependentAssembly>
- </dependency>
- </assembly>
|
到現在為止,所有的源文件都準備好了,接下來是編譯。為避免一篇文章過長,且看下回分解。 |
By now all source files are ready, and next job is compiling. For avoiding a too long article, please read the next part. |
|