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 阅读(139) | 评论 (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 阅读(136) | 评论 (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 阅读(496) | 评论 (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 阅读(2915) | 评论 (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 阅读(108) | 评论 (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 阅读(255) | 评论 (0)编辑 收藏
仅列出标题
共2页: 上一页 1 2