Eros Live
          Find the Way
          posts - 15,comments - 0,trackbacks - 0
          <2008年3月>
          2425262728291
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          在manifest的activity節(jié)點(diǎn)使用

          <activity android:windowSoftInputMode="adjustResize" . . . >

          當(dāng)點(diǎn)擊EditText控件彈出軟鍵盤的時候,系統(tǒng)會自動調(diào)整控件的位置。

          代碼

          http://github.com/shaobin0604/miscandroidapps/tree/master/WindowSoftInputMode/

          參考

          posted @ 2010-08-25 18:42 Eros 閱讀(272) | 評論 (0)編輯 收藏

          AppWidget的初始化有兩種方式:

          1. 沒有提供Configure Activity, 則在 AppWidgetProvider#onUpdate 里初始化。
          2. 提供Configure Activity, 則在 Configure Activity 里初始化。

          目前遇到的問題是:

          在Launcher里可以預(yù)先配置桌面顯示的AppWidget,如果AppWidget有Configure Activity,則系統(tǒng)在AppWidget的初始化過程不會發(fā)送android.appwidget.action.APPWIDGET_CONFIGURE Intent,而只是加載appwidget-provider里配置的initialLayout。這樣第二種就不可用,只能用第一種方法。

          posted @ 2010-08-24 11:11 Eros 閱讀(432) | 評論 (0)編輯 收藏

          1.字體大小

          synchronized void
          setTextSize(WebSettings.TextSize t)

          Set the text size of the page.

          2.縮放比例

          void
          setSupportZoom(boolean support)

          Set whether the WebView supports zoom

          void
          setInitialScale(int scaleInPercent)

          Set the initial scale for the WebView.

          boolean
          zoomIn()

          Perform zoom in in the webview

          boolean
          zoomOut()

          Perform zoom out in the webview

          3.縮放控件

          void
          setBuiltInZoomControls(boolean enabled)

          Sets whether the zoom mechanism built into WebView is used.

          4.JavaScript支持

          synchronized void
          setJavaScriptEnabled(boolean flag)

          Tell the WebView to enable javascript execution.

          posted @ 2010-07-28 12:49 Eros 閱讀(390) | 評論 (0)編輯 收藏

          在程序里備份恢復(fù)數(shù)據(jù)

          public static boolean backupDatabase() {
              File dbFile = new File(Environment.getDataDirectory() + "/data/" + PKG + "/databases/" + DB_NAME);
           
              File exportDir = new File(Environment.getExternalStorageDirectory(), "pocket-voa");
              
              if (!exportDir.exists()) {
                  exportDir.mkdirs();
              }
              
              File file = new File(exportDir, dbFile.getName());
           
              try {
                  file.createNewFile();
                  copyFile(dbFile, file);
                  return true;
              } catch (IOException e) {
                  Log.e(TAG, "[backupDatabase] error", e);
                  return false;
              }
          }
           
          public static boolean restoreDatabase() {
              File dbFile = new File(Environment.getDataDirectory() + "/data/" + PKG + "/databases/" + DatabaseHelper.DB_NAME);
           
              File exportDbFile = new File(Environment.getExternalStorageDirectory() + "/pocket-voa/" + DatabaseHelper.DB_NAME);
              
              if (!exportDbFile.exists())
                  return false;
           
              try {
                  dbFile.createNewFile();
                  copyFile(exportDbFile, dbFile);
                  return true;
              } catch (IOException e) {
                  Log.e(TAG, "[restoreDatabase] error", e);
                  return false;
              }
          }
           
          private static void copyFile(File src, File dst) throws IOException {
              FileChannel inChannel = new FileInputStream(src).getChannel();
              FileChannel outChannel = new FileOutputStream(dst).getChannel();
              try {
                  inChannel.transferTo(0, inChannel.size(), outChannel);
              } finally {
                  if (inChannel != null)
                      inChannel.close();
                  if (outChannel != null)
                      outChannel.close();
              }
          }

          參考

          posted @ 2010-07-26 17:24 Eros 閱讀(345) | 評論 (0)編輯 收藏

           

          There are certain events that Android does not want to start up new processes for, so the device does not get too slow from all sorts of stuff all having to run at once. ACTION_SCREEN_ON is one of those. See this previous question for light blue advice on that topic.

          So, you need to ask yourself, "Self, do I really need to get control on those events?". The core Android team would like it if your answer was "no".

          posted @ 2010-07-22 19:59 Eros 閱讀(1178) | 評論 (0)編輯 收藏
               摘要: 1.海詞 http://api.dict.cn/ws.php?utf8=true&q=#{word} 返回格式XML<?xml version="1.0" encoding="UTF-8" ?> <dict> <key>word</key> <lang>ec</lang> <audio>...  閱讀全文
          posted @ 2010-07-15 15:45 Eros 閱讀(2038) | 評論 (0)編輯 收藏

          打開終端輸入

          adb devices

          出現(xiàn)如下內(nèi)容

          ??????????? no permissions

          原因是啟動adb的時候需要有root權(quán)限。如果一開始忘記加了sudo, 就必須先終止adb。

          $ adb kill-server

          $ sudo adb start-server

          $ adb devices

          就可以看到設(shè)備信息了。

          參考

          posted @ 2010-07-07 15:44 Eros 閱讀(597) | 評論 (0)編輯 收藏
               摘要: 原理 關(guān)閉APN的原理是在APN信息表(content://telephony/carriers/current)的apn, type字段添加自定義的后綴(參考自APNDroid) 代碼 (取自 Quick Settings) package com.android.settings.widget; import android.content.ContentResolver;impo...  閱讀全文
          posted @ 2010-07-07 15:28 Eros 閱讀(724) | 評論 (0)編輯 收藏

          環(huán)境

          Ubuntu 9.10 64bit, sun-jdk-1.5(因需要編譯Android源代碼), Android SDK 2.1

          癥狀

          draw9patch 不能正確顯示出窗口,沒有菜單欄

          原因

          sun jdk 1.5 的BUG

          解決

          安裝 sun jdk 1.6

          參考

          1. http://resources2.visual-paradigm.com/index.php/tips-support/53-support/61-blank-screen.html
          2. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6585673
          3. https://bugs.launchpad.net/ubuntu/+bug/164004
          4. http://forums.visual-paradigm.com/posts/list/6719.html
          posted @ 2010-07-07 12:19 Eros 閱讀(274) | 評論 (0)編輯 收藏

          Install Ubuntu 9.10


          Links

          1. http://docs.google.com/fileview?id=0B7vaQCSPJU8PNjUzZmU1ZTItYTVlNi00ZDBmLWFhMzMtN2Q3NDA4MzljMjRm&hl=zh_CN
          2. http://ubuntuabc.com/123/?p=38

          Install JDK

          sudo apt-get install sun-java6-jdk

          sudo update-alternatives –config java


          posted @ 2010-07-02 19:58 Eros 閱讀(300) | 評論 (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 閱讀(146) | 評論 (0)編輯 收藏

          1.文本編輯器

           

          2.文本查看器

          JSON

          3.正則表達(dá)式

          在線測試工具

          4.字體

          YaHei Consolas

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

          設(shè)置字體大小為五號

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

          posted @ 2010-06-30 18:51 Eros 閱讀(142) | 評論 (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節(jié)點(diǎn)加入如下屬性

          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卡寫入權(quán)限

          在AndroidManifest.xml加入以下代碼

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

          SDK1.6之前的項(xiàng)目自適應(yīng),無需此權(quán)限也可以寫入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設(shè)置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 事件無法觸發(fā)。
          原因:checkbox的優(yōu)先級高于ListItem于是屏蔽了ListItem的單擊事件。
          解決方案:設(shè)置checkbox的android:focusable="false"

          10.取得當(dāng)前的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.調(diào)整WebView字體大小

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

          13.View Animation總結(jié)

          參考

          14.檢查網(wǎng)絡(luò)狀態(tài)

          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();    
          }

          需要加入權(quán)限

          <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.設(shè)置EditText的輸入模式

          url, password, email, 電話鍵盤等

          設(shè)置 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 閱讀(503) | 評論 (0)編輯 收藏

          實(shí)現(xiàn)應(yīng)用《cnBeta業(yè)界資訊》分批加載數(shù)據(jù)的效果

          image image image

          這里L(fēng)istView底部的“顯示后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 閱讀(2920) | 評論 (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個線程執(zhí)行”的意思。就像細(xì)獨(dú)木橋只允許一個人通行一樣,這個Pattern用來限制同時只讓一個線程運(yùn)行。

          Single Threaded Execution將會是多線程程序設(shè)計(jì)的基礎(chǔ)。務(wù)必要學(xué)好。

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

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

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

          首先,我們先來看一個應(yīng)該要使用Single Threaded Execution Pattern而沒有使用和程序范例。這個程序的用意是要實(shí)際體驗(yàn)多線程無法正確執(zhí)行的程序,會發(fā)生什么現(xiàn)象。

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

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

          Main類

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

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

          Alice - Alaska
          Bobby - Brazil
          Chris - Canada

          為了便于對應(yīng)兩者之間的關(guān)系,筆者在此故意將姓名與出生地設(shè)成相同的開頭字母。

          在上線程中,先創(chuàng)建3個UserThread類的實(shí)例,并以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字段表示目前已經(jīng)通過這道門的“人數(shù)”。name字段表示通過門的行人的“姓名”,而address字段則表示通過者的“出生地”

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

          this.name = name;

          toString方法,會以字符串的形式返回現(xiàn)在門的狀態(tài)。使用現(xiàn)在的counter、name、address各字段的值,創(chuàng)建字符串。

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

          ****** BROKEN ******
          并接著調(diào)用toString方法顯示出現(xiàn)在門的狀態(tài)。

          這個Gate類,在單線程的時候可以正常運(yùn)行,但是在多線程下就無法正常執(zhí)行。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);
          ??????????? }
          ??????? }
          ??? }

          為什么會出錯呢?

          這是因?yàn)镚ate類的pass方法會被多個線程調(diào)用的關(guān)系。pass方法是下面4行語句程序代碼所組成:

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

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

          圖1-3 線程Alice與線程Bobby調(diào)用pass方法時的執(zhí)行狀況
          -----------------------------------------------------------------------------------
          線程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所示,則調(diào)用check方法的時刻,name的值是"Bobby",而address的值會是"Alaska"。這個時候也會顯示出BROKEN。

          圖1-4 線程Alice與線程Bobby調(diào)用pass方法的執(zhí)行狀況
          ------------------------------------------------------------------------------------
          線程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的值出現(xiàn)非預(yù)期的結(jié)果。
          通常,線程不會去考慮其他的線程,而自己只會一直不停地跑下去。“線程Alice現(xiàn)在執(zhí)行到的位置正指定name結(jié)束,還沒有指定address的值”,而線程Bobby對此情況并不知情。

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


          以上是沒有使用Single Threaded Execution Pattern時所發(fā)生的現(xiàn)象。

          范例程序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所扮演的角色

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

          Single Threaded Execution Pattern的所有參與者

          SharedResource(共享資源)參與者

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

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

          SafeMethod?? - 從多個線程同時調(diào)用也不會發(fā)生問題的方法
          UnsafeMethod - 從多個線程同時調(diào)用會出問題,而需要加以防護(hù)的方法。

          在Single Threaded Execution Pattern中,我們將UnsafeMethod加以防衛(wèi),限制同時只能有一個線程可以調(diào)用它。在Java語言中,只要將UnsafeMethod定義成synchronized方法,就可以實(shí)現(xiàn)這個目標(biāo)。

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

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

          擴(kuò)展思考方向的提示

          何時使用(適用性)

          多線程時

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

          數(shù)據(jù)可以被多個線程訪問的時候

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

          狀態(tài)可能變化的時候

          當(dāng)SharedResource參與者狀態(tài)可能變化的時候,才會有使用Single Threaded Execution Pattern的需要。
          如果實(shí)例創(chuàng)建之后,從此不會改變狀態(tài),也沒有用用Single Threaded Execution Pattern的必要。

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

          需要確保安全性的時候

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

          生命性與死鎖

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

          舉個例子:

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

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

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

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

          Single Threaded Execution達(dá)到下面這些條件時,可能會出現(xiàn)死鎖的現(xiàn)象。

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

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

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

          ?

          問題1-7

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

          順帶一提,像Mutex類這種用來進(jìn)行共享互斥的機(jī)制,一般稱為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,就表示執(zhí)行了lock;如果busy是false,則表示執(zhí)行了unlock方法。lock與unlock雙方都已是synchronized方法保護(hù)著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會正確地執(zhí)行。但是,若使用于其他用途,則會發(fā)生如下問題。這就是對使用Mutex類的限制。這意味著Mutex類的重復(fù)使用性上會有問題。

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

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

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

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

          代碼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 閱讀(273) | 評論 (0)編輯 收藏
          主站蜘蛛池模板: 普安县| 西盟| 宜君县| 杭锦后旗| 西乌珠穆沁旗| 库车县| 靖西县| 西丰县| 海原县| 涿州市| 乳山市| 庆云县| 鹤峰县| 沁阳市| 斗六市| 吴江市| 滦南县| 肃宁县| 同仁县| 呈贡县| 社旗县| 兴国县| 饶阳县| 东乌| 永修县| 灵石县| 丰顺县| 南通市| 南部县| 桦川县| 库车县| 开封市| 普兰店市| 仁化县| 和硕县| 朔州市| 繁峙县| 长泰县| 丹巴县| 广宁县| 德昌县|