Eros Live
          Find the Way
          posts - 15,comments - 0,trackbacks - 0

          1.NPR News

          http://code.google.com/p/npr-android-app/

          image

          2.Quick Settings

          Quick Settings is a highly customizable all-in-one settings applications for Android.

          http://code.google.com/p/quick-settings/

          image

          3.Quick Battery

          http://code.google.com/p/quick-settings/source/browse/#svn/trunk/quick-battery

          posted @ 2010-07-01 18:52 Eros 閱讀(145) | 評論 (0)編輯 收藏

          1.文本編輯器

           

          2.文本查看器

          JSON

          3.正則表達式

          在線測試工具

          4.字體

          YaHei Consolas

          http://www.box.net/shared/72dcnre8on

          設置字體大小為五號

          參考http://be-evil.org/post-178.html

          posted @ 2010-06-30 18:51 Eros 閱讀(141) | 評論 (0)編輯 收藏

          1.獲取屏幕的分辨率

          在 Activity 里使用如下代碼,寬度和高度的單位是像素

          Display display = getWindowManager().getDefaultDisplay();
          int screenWidth = display.getWidth();
          int screenHeight = display.getHeight();

          2.繪制文本

          使用 FontMetrics 類

          參考

          http://www.javaeye.com/topic/474526

          3.禁止自動橫豎屏切換

          在AndroidManifest.xml的Activity節點加入如下屬性

          android:screenOrientation="portrait"

          portrait是縱向,landscape是橫向

          4.Resources and Assets

          無論是使用Res\raw還是使用Asset存儲資源文件,文件大小UNCOMPRESS限制為1MB

          參考

          http://wayfarer.javaeye.com/blog/547174

          5.SDK 1.6 新增加SD卡寫入權限

          在AndroidManifest.xml加入以下代碼

          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

          SDK1.6之前的項目自適應,無需此權限也可以寫入SD卡。

          6.在eclipse中查看Android Framework源代碼

          代碼下載

          1. SDK 1.5 http://www.box.net/shared/16au19tqlp
          2. SDK 1.6 http://www.box.net/shared/dh4dr9ir7j
          3. SDK 2.2 http://www.box.net/shared/dic2t0blj1

          參考

          http://www.javaeye.com/topic/534010

          7.給View注冊ContextMenu

          void setOnCreateContextMenuListener(View.OnCreateContextMenuListener l)

          Register a callback to be invoked when the context menu for this view is being built.

          8.給Preference設置Intent

          void setIntent(Intent intent)

          Sets an Intent to be used for startActivity(Intent) when this Preference is clicked.

          9.包含CheckBox的ListView

          ListView item中加入checkbox后onListItemClick 事件無法觸發。
          原因:checkbox的優先級高于ListItem于是屏蔽了ListItem的單擊事件。
          解決方案:設置checkbox的android:focusable="false"

          10.取得當前的Locale

          Locale locale = context.getResources().getConfiguration().locale;

          11.使用android.text.format.Time類代替java.util.Calendar類

          The Time class is a faster replacement for the java.util.Calendar and java.util.GregorianCalendar classes. An instance of the Time class represents a moment in time, specified with second precision. It is modelled after struct tm, and in fact, uses struct tm to implement most of the functionality.

          12.調整WebView字體大小

          WebView.getSettings().setDefaultFontSize()
          WebView.getSettings().setDefaultZoom()

          13.View Animation總結

          參考

          14.檢查網絡狀態

          public static boolean isNetworkAvailable(Context context) {
              ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
              NetworkInfo info = cm.getActiveNetworkInfo();
              
              return (info != null && info.isConnected());
          }
           
           
          public static boolean isWifiConnected(Context context) { 
              return getNetworkState(context, ConnectivityManager.TYPE_WIFI) == State.CONNECTED;
          }
           
          public static boolean isMobileConnected(Context context) {
              return getNetworkState(context, ConnectivityManager.TYPE_MOBILE) == State.CONNECTED;
          }
           
          private static State getNetworkState(Context context, int networkType) {
              ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 
              NetworkInfo info = cm.getNetworkInfo(networkType);
              
              return info == null ? null : info.getState();    
          }

          需要加入權限

          <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

          15.Parse JSON String

          參考

          1. http://www.androidcompetencycenter.com/2009/10/json-parsing-in-android/
          2. http://wiki.fasterxml.com/JacksonInFiveMinutes
          3. http://stackoverflow.com/questions/2818697/sending-and-parsing-json-in-android

          16.設置EditText的輸入模式

          url, password, email, 電話鍵盤等

          設置 android:inputType 屬性

          17.Crash Report

          1. http://code.google.com/p/acra/
          2. http://code.google.com/p/android-send-me-logs/

          18.用戶解鎖消息

          android.intent.action.USER_PRESENT

          只能在代碼里注冊Receiver

          19.屏幕消息

          android.intent.action.SCREEN_ON

          android.intent.action.SCREEN_OFF

          只能在代碼里注冊Receiver

          posted @ 2010-06-29 13:22 Eros 閱讀(502) | 評論 (0)編輯 收藏

          實現應用《cnBeta業界資訊》分批加載數據的效果

          image image image

          這里ListView底部的“顯示后10條”Button用到了ListView的方法

          public void addFooterView (View v)

          布局文件 main.xml

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              >
              <ListView android:id="@+id/list" 
                  android:layout_width="fill_parent" 
                  android:layout_height="wrap_content" 
                  android:layout_weight="1"/>
          </LinearLayout>

          MainActivity.java

          package com.example.listviewwithheaderandfooter;
           
          import java.util.ArrayList;
          import java.util.List;
           
          import android.app.Activity;
          import android.app.ProgressDialog;
          import android.os.Bundle;
          import android.view.View;
          import android.widget.ArrayAdapter;
          import android.widget.Button;
          import android.widget.ListView;
           
          public class MainActivity extends Activity {
              static final String[] COUNTRIES = new String[] {
                  "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
                  "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
                  "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
                  "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
                  "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
                  "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
                  "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
                  "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
                  "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
                  "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
                  "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
                  "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
                  "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
                  "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
                  "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
                  "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
                  "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
                  "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
                  "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
                  "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
                  "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
                  "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
                  "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
                  "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
                  "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
                  "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
                  "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
                  "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
                  "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
                  "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
                  "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
                  "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
                  "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
                  "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
                  "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
                  "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
                  "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
                  "Ukraine", "United Arab Emirates", "United Kingdom",
                  "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
                  "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
                  "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
                };
              
              private List<String> mList = new ArrayList<String>();
           
              private ArrayAdapter<String> mAdapter;
              
              
              /** Called when the activity is first created. */
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.main);
                  
                  ListView list = (ListView) findViewById(R.id.list);
                  
                  Button buttonHeader = new Button(this);
                  buttonHeader.setText("remove 1 entries");
                  buttonHeader.setOnClickListener(new View.OnClickListener() {
                      
                      @Override
                      public void onClick(View v) {
                          if (mList.size() > 0) {
                              mList.remove(mList.size() - 1);
                              mAdapter.notifyDataSetChanged();
                          }
                      }
                  });
                  list.addHeaderView(buttonHeader);
                  
                  Button buttonFooter = new Button(this);
                  buttonFooter.setText("add 1 entries");
                  
                  buttonFooter.setOnClickListener(new View.OnClickListener() {
                      
                      @Override
                      public void onClick(View v) {
                          // TODO Auto-generated method stub
                          final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
                          dialog.setIndeterminate(true);
                          dialog.show();
                          new Thread() {
                              public void run() {
                                  int start = mList.size();
                                  int end = start + 1;
                                  if (end > COUNTRIES.length)
                                      end = COUNTRIES.length;
                                  
                                  for (; start < end; start++) {
                                      mList.add(COUNTRIES[start]);
                                  }
                                  
                                  runOnUiThread(new Runnable() {
                                      
                                      @Override
                                      public void run() {
                                          dialog.dismiss();
                                          mAdapter.notifyDataSetChanged();
                                      }
                                  });
                                  
                              }
                          }.start();
                          
                          
                      }
                  });
                  list.addFooterView(buttonFooter);
                  
                  mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mList);
                  
                  list.setAdapter(mAdapter);
              }
          }

          效果

          image image

          posted @ 2010-06-29 11:20 Eros 閱讀(2918) | 評論 (0)編輯 收藏

          http://stackoverflow.com/questions/2691570/android-how-to-properly-handle-onpause-onresume-methods

          http://stackoverflow.com/questions/151777/how-do-i-save-an-android-applications-state

          http://stackoverflow.com/questions/2441145/onpause-onresume-activity-issues

           

          sprite

          posted @ 2010-05-21 09:45 Eros 閱讀(112) | 評論 (0)編輯 收藏
          Single Threaded Execution是指“以1個線程執行”的意思。就像細獨木橋只允許一個人通行一樣,這個Pattern用來限制同時只讓一個線程運行。

          Single Threaded Execution將會是多線程程序設計的基礎。務必要學好。

          Single Threaded Execution有時候也被稱為Critical Section(臨界區)。

          Single Threaded Execution是把視點放在運行的線程(過橋的人)上所取的名字,而Critical Section則是把視點放在執行的范圍(橋身)上所取的名字。

          范例程序1:不使用Single Threaded Execution Pattern的范例

          首先,我們先來看一個應該要使用Single Threaded Execution Pattern而沒有使用和程序范例。這個程序的用意是要實際體驗多線程無法正確執行的程序,會發生什么現象。

          模擬3個人頻繁地經過一個只能容許一個人經過的門。當人通過門的時候,這個程序會在計數器中遞增通過的人數。另外,還會記錄通過的人的“姓名與出生地”

          表1-1 類一覽表
          --------------------------------------------------------------
          ?名稱??   ??????????????????? 說明
          --------------------------------------------------------------
          Main??????????  創建一個門,并操作3個人不斷地穿越門的類
          Gate?  ???????? 表示門的類,當人經過時會記錄下姓名與出身地
          UserThread?????? 表示人的類,只負責處理不斷地在門間穿梭通過
          --------------------------------------------------------------

          Main類

          Main類(List 1-1)用來創建一個門(Gate),并讓3個人(UserThread)不斷通過。創建Gate對象的實例,并將這個實例丟到UserThread類的構造器作為參數,告訴人這個對象“請通過這個門”。

          有下面3個人會通過這個門:

          Alice - Alaska
          Bobby - Brazil
          Chris - Canada

          為了便于對應兩者之間的關系,筆者在此故意將姓名與出生地設成相同的開頭字母。

          在上線程中,先創建3個UserThread類的實例,并以start方法啟動這些線程。

          List 1-1 Main.java
          --------------------------------------
          public class Main {
          ??? public static void main(String[] args) {
          ??????? System.out.println("Testing Gate, hit CTRL+C to exit.");
          ??????? Gate gate = new Gate();
          ??????? new UserThread(gate, "Alice", "Alaska").start();
          ??????? new UserThread(gate, "Bobby", "Brazil").start();
          ??????? new UserThread(gate, "Chris", "Canada").start();
          ??? }
          }
          --------------------------------------

          并非線程安全的(thread-safe)的Gate類

          Gate類(List 1-2)表示人所要通過的門。

          counter字段表示目前已經通過這道門的“人數”。name字段表示通過門的行人的“姓名”,而address字段則表示通過者的“出生地”

          pass是穿越這道門時使用的方法。在這個方法中,會將表示通過人數的counter字段的值遞增1,并將參數中傳入行人的姓名與出生地,分別拷貝到name字段與address字段中。

          this.name = name;

          toString方法,會以字符串的形式返回現在門的狀態。使用現在的counter、name、address各字段的值,創建字符串。

          check方法,用來檢查現在門的狀態(最后通過的行人的記錄數據)是否正確。當人的姓名(name)與出生地(address)第一個字符不相同時,就斷定記錄是有問題的。當發現記錄有問題時,就顯示出下面的字符串:

          ****** BROKEN ******
          并接著調用toString方法顯示出現在門的狀態。

          這個Gate類,在單線程的時候可以正常運行,但是在多線程下就無法正常執行。List 1-2 的Gate類是缺乏安全性的類,并不是線程安全(thread-safe)的類。

          List 1-1 非線程安全的Gate類(Gate.java)

          ??? public class Gate {
          ??????? private int counter = 0;
          ??????? private String name = "Nobody";
          ??????? private String address = "Nowhere";
          ??????? public void pass(String name, String address) {
          ??????????? this.counter++;
          ??????????? this.name = name;
          ??????????? this.address = address;
          ??????????? check();
          ??????? }
          ??????? public String toString() {
          ??????????? return "No." + counter + ": " + name + ", " + address;
          ??????? }
          ??????? private void check() {
          ??????????? if (name.charAt(0) != address.charAt(0)) {
          ??????????????? System.out.println("***** BROKEN ***** " + toString());
          ??????????? }
          ??????? }
          ??? }

          UserThread類

          UserThread類(List 1-3)表示不斷穿越門的行人。這個類被聲明成Thread類的子類。

          List 1-3 UserThread.java

          ??? public class UserThread extends Thread {
          ??????? private final Gate gate;
          ??????? private final String myname;
          ??????? private final String myaddress;
          ??????? public UserThread(Gate gate, String myname, String myaddress) {
          ??????????? this.gate = gate;
          ??????????? this.myname = myname;
          ??????????? this.myaddress = myaddress;
          ??????? }
          ??????? public void run() {
          ??????????? System.out.println(myname + " BEGIN");
          ??????????? while (true) {
          ??????????????? gate.pass(myname, myaddress);
          ??????????? }
          ??????? }
          ??? }

          為什么會出錯呢?

          這是因為Gate類的pass方法會被多個線程調用的關系。pass方法是下面4行語句程序代碼所組成:

          this.counter++;
          this.name = name;
          this.address = address;
          check();

          為了在解說的時候簡單一點,現在只考慮兩個線程(Alice和Bobby)。兩個線程調用pass方法時,上面4行語句可能會是交錯依次執行。如果交錯的情況是圖1-3這樣,那調用check方法的時候,name的值會是“Alice”,而address的值會是“Brazil”。這時就會顯示出 BROKEN了。

          圖1-3 線程Alice與線程Bobby調用pass方法時的執行狀況
          -----------------------------------------------------------------------------------
          線程Alice?????????????? 線程Bobby?????????????? this.name的值??????? this.address的值
          -----------------------------------------------------------------------------------
          this.counter++???????? this.counter++
          ?????????????????????????????? this.name = name??????? "Bobby"??
          this.name = name?????????????????????????????????????? "Alice"
          this.address = address??????????????????????????????? "Alice"???????????? "Alaska"
          ????????????????????????????? this.address = address? "Alice"???????????? "Brazil"
          check()???????????????? ?check()???????????????????????? "Alice"???????????? "Brazil"
          ****** BROKEN ******
          -----------------------------------------------------------------------------------

          或者說交錯的情況如圖1-4所示,則調用check方法的時刻,name的值是"Bobby",而address的值會是"Alaska"。這個時候也會顯示出BROKEN。

          圖1-4 線程Alice與線程Bobby調用pass方法的執行狀況
          ------------------------------------------------------------------------------------
          線程Alice????????????? 線程Bobby??????????????? this.name的值??????? this.address的值
          ------------------------------------------------------------------------------------
          this.counter++??????? this.counter++?
          this.name = name??????????????????????????????????????? "Alice"
          ???????????????????????????? this.name = name????????? ?"Bobby"
          ??????????????????????????? ?this.address = address??? "Bobby"????????????? "Brazil"
          this.address = address???????????????????????????????? "Bobby"????????????? "Alaska"
          check()???????????????? check()?????????????????????????? "Bobby"????????????? "Alaska"
          ****** BROKEN ******
          ------------------------------------------------------------------------------------

          上述哪一種情況,都使字段name與address的值出現非預期的結果。
          通常,線程不會去考慮其他的線程,而自己只會一直不停地跑下去。“線程Alice現在執行到的位置正指定name結束,還沒有指定address的值”,而線程Bobby對此情況并不知情。

          范例程序1之所以會顯示出BROKEN,是因為線程并沒有考慮到其他線程,而將共享實例的字段改寫了。
          對于name字段來說,有兩個線程在比賽,贏的一方先將值改寫。對address來說,也有兩個線程在比賽誰先將值改寫。像這樣子引發競爭(race)的狀況,我們稱為race condition。有race condition的情況時,就很難預測各字段的值了。


          以上是沒有使用Single Threaded Execution Pattern時所發生的現象。

          范例程序2:使用Single Threaded Execution Pattern的范例

          線程安全的Gate類
          List 1-4 是線程安全的Gate類。需要修改的有兩個地方,在pass方法與toString方法前面都加上synchronized。這樣Gate類就成為線程安全的類了。

          List 1-4 線程安全的Gate類(Gate.java)

          ??? public class Gate {
          ??????? private int counter = 0;
          ??????? private String name = "Nobody";
          ??????? private String address = "Nowhere";
          ??????? public synchronized void pass(String name, String address) {
          ??????????? this.counter++;
          ??????????? this.name = name;
          ??????????? this.address = address;
          ??????????? check();
          ??????? }
          ??????? public synchronized String toString() {
          ??????????? return "No." + counter + ": " + name + ", " + address;
          ??????? }
          ??????? private void check() {
          ??????????? if (name.charAt(0) != address.charAt(0)) {
          ??????????????? System.out.println("***** BROKEN ***** " + toString());
          ??????????? }
          ??????? }
          ??? }

          synchronized所扮演的角色

          如前面一節所說,非線程安全的Gate類之所以會顯示BROKEN, 是因為pass方法內的程序代碼可以被多個線程穿插執行。
          synchronized 方法,能夠保證同時只有一個線程可以執行它。這句話的意思是說:線程Alice執行pass方法的時候,線程Bobby就不能調用pass方法。在線程 Alice執行完pass方法之前,線程Bobby會在pass方法的入口處被阻擋下。當線程Alice執行完pass方法之后,將鎖定解除,線程 Bobby才可以開始執行pass方法。

          Single Threaded Execution Pattern的所有參與者

          SharedResource(共享資源)參與者

          Single Threaded Execution Pattern中,有擔任SharedResource角色的類出現。在范例程序2中,Gate類就是這個SharedResource參與者。

          SharedResource參與者是可以由多個線程訪問的類。SharedResource會擁有兩類方法:

          SafeMethod?? - 從多個線程同時調用也不會發生問題的方法
          UnsafeMethod - 從多個線程同時調用會出問題,而需要加以防護的方法。

          在Single Threaded Execution Pattern中,我們將UnsafeMethod加以防衛,限制同時只能有一個線程可以調用它。在Java語言中,只要將UnsafeMethod定義成synchronized方法,就可以實現這個目標。

          這個必須只讓單線程執行的程序范圍,被稱為臨界區(critical section)

          ???????????????????????????????????????????????? :SharedResource
          ---------?????????????????????????????????? -----------------
          :Thread? -----------------------|-> synchronized|
          ---------???????????????????????????????? ?|? UnsafeMethod1|
          ????????????????????????????????????????????? ?|????????????????????????? ?|
          ---------????????????????????????????????? |?????????????????????????? |
          :Thread? ---------------------->|?? synchronized|
          ---------?????????????????????????????????? |? UnsafeMethod2|
          ???????????????????????????????????????????????? -----------------

          擴展思考方向的提示

          何時使用(適用性)

          多線程時

          單線程程序,并不需要使用Single Threaded Execution Pattern。因此,也不需要使用到synchronized方法。

          數據可以被多個線程訪問的時候

          會需要使用Single Threaded Execution Pattern的情況,是在SharedResource的實例可能同時被多個線程訪問的時候。
          就算是多線程程序,如果所有線程完全獨立運行,那也沒有使用Single Threaded Execution Pattern的必要。我們將這個狀態稱為線程互不干涉(interfere)。
          有些管理多線程的環境,會幫我們確保線程的獨立性,這種情況下這個環境的用戶就不必考慮需不需要使用Single Thread Execution Pattern。

          狀態可能變化的時候

          當SharedResource參與者狀態可能變化的時候,才會有使用Single Threaded Execution Pattern的需要。
          如果實例創建之后,從此不會改變狀態,也沒有用用Single Threaded Execution Pattern的必要。

          第二章所要介紹的Immutable Pattern就是這種情況。在Immutable Pattern中,實例的狀態不會改變,所以是不需要用到synchronized方法的一種Pattern。

          需要確保安全性的時候

          只有需要確保安全性的時候,才會需要使用Single Threaded Execution Pattern。
          例如,Java的集合架構類多半并非線程安全。這是為了在不考慮安全性的時候獲得更好的性能。
          所以用戶需要考慮自己要用的類需不需要考慮線程安全再使用。

          生命性與死鎖

          使用Single Threaded Execution Pattern時,可能會發生死鎖(deadlock)的危險。
          所謂死鎖,是指兩個線程分別獲取了鎖定,互相等待另一個線程解除鎖定的現象。發生死鎖的時,兩個線程都無法繼續執行下去,所以程序會失去生命性。

          舉個例子:

          假設Alice與Bobby同吃一個大盤子所盛放的意大利面。盤子的旁邊只有一支湯匙和一支叉子,而要吃意大利面時,需要同時用到湯匙與叉子。

          只有一支的湯匙,被Alice拿去了,而只有一支的叉子,去被Bobby拿走了。就造成以下的情況:

          握著湯匙的Alice,一直等著Bobby把叉子放下。
          握著叉子的Bobby,一直等著Alice的湯匙放下。

          這么一來Alice和Bobby只有面面相覷,就這樣不動了。像這樣,多個線程僵持不下,使程序無法繼續運行的狀態,就稱為死鎖。

          Single Threaded Execution達到下面這些條件時,可能會出現死鎖的現象。

          1.具有多個SharedResource參與者
          2.線程鎖定一個SharedResource時,還沒有解除鎖定就前去鎖定另一個SharedResource。
          3.線程獲取SharedResource參與者的順序不固定(和SharedResource參與者對等的)。

          回過頭來看前面吃不到意大利面的兩個人這個例子。
          1.多個SharedResource參與者,相當于湯匙和叉子。
          2.鎖定某個SharedResource的參與者后,就去鎖定其他SharedResource。就相當于握著湯匙而想要獲取對方的叉子,或握著叉子而想要獲取對方的湯匙這些操作。
          3.SharedResource角色是對等的,就像“拿湯匙->拿叉子”與“拿叉子->拿湯匙”兩個操作都可能發生。也就是說在這里湯匙與叉子并沒有優先級。

          1, 2, 3中只要破壞一個條件,就可以避免死鎖的發生。具體的程序代碼如問題1-6

          ?

          問題1-7

          某人正思考著若不使用synchronized,有沒有其他的方法可以做到Single Threaded Execution Pattern。而他寫下了如下的Gate類,如代碼1。那么接下來就是問題。請創建Gate類中所要使用的Mutex類。

          順帶一提,像Mutex類這種用來進行共享互斥的機制,一般稱為mutex。mutex是mutual exclusion(互斥)的簡稱。

          代碼1

          ??? public class Gate {
          ??????? private int counter = 0;
          ??????? private String name = "Nobody";
          ??????? private String address = "Nowhere";
          ??????? private final Mutex mutex = new Mutex();
          ??????? public void pass(String name, String address) { // 并非synchronized
          ??????????? mutex.lock();
          ??????????? try {
          ??????????????? this.counter++;
          ??????????????? this.name = name;
          ??????????????? this.address = address;
          ??????????????? check();
          ??????????? } finally {
          ??????????????? mutex.unlock();
          ??????????? }
          ??????? }
          ??????? public String toString() { //? 并非synchronized
          ??????????? String s = null;
          ??????????? mutex.lock();
          ??????????? try {
          ??????????????? s = "No." + counter + ": " + name + ", " + address;
          ??????????? } finally {
          ??????????????? mutex.unlock();
          ??????????? }
          ??????????? return s;
          ??????? }
          ??????? private void check() {
          ??????????? if (name.charAt(0) != address.charAt(0)) {
          ??????????????? System.out.println("***** BROKEN ***** " + toString());
          ??????????? }
          ??????? }
          ??? }


          解答范例1:單純的Mutex類

          下面是最簡單的 Mutex類,如代碼2。在此使用busy這個boolean類型的字段。busy若是true,就表示執行了lock;如果busy是false,則表示執行了unlock方法。lock與unlock雙方都已是synchronized方法保護著busy字段。

          代碼2

          ??? public final class Mutex {
          ??????? private boolean busy = false;
          ??????? public synchronized void lock() {
          ??????????? while (busy) {
          ??????????????? try {
          ??????????????????? wait();
          ??????????????? } catch (InterruptedException e) {
          ??????????????? }
          ??????????? }
          ??????????? busy = true;
          ??????? }
          ??????? public synchronized void unlock() {
          ??????????? busy = false;
          ??????????? notifyAll();
          ??????? }
          ??? }

          代碼2所示的Mutex類在問題1-7會正確地執行。但是,若使用于其他用途,則會發生如下問題。這就是對使用Mutex類的限制。這意味著Mutex類的重復使用性上會有問題。

          問題點1
          假設有某個線程連續兩次調用lock方法。調用后,在第二次調用時,由于busy字段已經變成true,因此為wait。這就好像自己把自己鎖在外面,進不了門的意思一樣。

          問題點2
          即使是尚未調用出lock方法的線程,也會變成可以調用unlock方法。就好比即使不是自己上的鎖,自己還是可以將門打開一樣。

          解答范例2:改良后的Mutex類

          代碼3是將類似范例1中的問題予以改良而形成的新的Mutex類。在此,現在的lock次數記錄在locks字段中。這個lock數是從在lock方法調用的次數扣掉在 unlock方法調用的次數得出的結果。連調用lock方法的線程也記錄在owner字段上。我們現在用locks和owner來解決上述的問題點。

          代碼3

          ??? public final class Mutex {
          ??????? private long locks = 0;
          ??????? private Thread owner = null;
          ??????? public synchronized void lock() {
          ??????????? Thread me = Thread.currentThread();
          ??????????? while (locks > 0 && owner != me) {
          ??????????????? try {
          ??????????????????? wait();
          ??????????????? } catch (InterruptedException e) {
          ??????????????? }
          ??????????? }
          ??????????? //assert locks == 0 || owner == me
          ??????????? owner = me;
          ??????????? locks++;
          ??????? }
          ??????? public synchronized void unlock() {
          ??????????? Thread me = Thread.currentThread();
          ??????????? if (locks == 0 || owner != me) {
          ??????????????? return;
          ??????????? }
          ??????????? //assert locks > 0 && owner == me
          ??????????? locks--;
          ??????????? if (locks == 0) {
          ??????????????? owner = null;
          ??????????????? notifyAll();
          ??????????? }
          ??????? }
          ??? }


          測試代碼

          代碼4

          ??? public class UserThread extends Thread {
          ??????? private final Gate gate;
          ??????? private final String myname;
          ??????? private final String myaddress;
          ??????? public UserThread(Gate gate, String myname, String myaddress) {
          ??????????? this.gate = gate;
          ??????????? this.myname = myname;
          ??????????? this.myaddress = myaddress;
          ??????? }
          ??????? public void run() {
          ??????????? System.out.println(myname + " BEGIN");
          ??????????? while (true) {
          ??????????????? gate.pass(myname, myaddress);
          ??????????? }
          ??????? }
          ??? }

          代碼5

          ??? public class Main {
          ??????? public static void main(String[] args) {
          ??????????? System.out.println("Testing Gate, hit CTRL+C to exit.");
          ??????????? Gate gate = new Gate();
          ??????????? new UserThread(gate, "Alice", "Alaska").start();
          ??????????? new UserThread(gate, "Bobby", "Brazil").start();
          ??????????? new UserThread(gate, "Chris", "Canada").start();
          ??????? }
          ??? }
          posted @ 2008-03-08 14:54 Eros 閱讀(271) | 評論 (0)編輯 收藏
          僅列出標題
          共2頁: 上一頁 1 2 
          主站蜘蛛池模板: 天等县| 金秀| 巢湖市| 陆良县| 张家口市| 桓仁| 桂平市| 大名县| 嘉祥县| 浠水县| 五家渠市| 滕州市| 连平县| 克拉玛依市| 磐安县| 盘锦市| 陵川县| 岐山县| 嵩明县| 大悟县| 巢湖市| 南投县| 荥经县| 长岛县| 昭苏县| 安国市| 神农架林区| 南和县| 虞城县| 邯郸市| 永安市| 荔波县| 江源县| 道孚县| 永年县| 柳河县| 从化市| 罗定市| 蕉岭县| 德阳市| 武隆县|