[Go Back]

[請指教:高老師的免費on-line教學視頻] 

 

1.4.2  Thread造形与Activity造形的美丽组合

复习Thread框架造形

      前面介绍过了Thread框架,也就是{Thread, Runnable, <T>}造形。如下图所示: 

 

  图1-20 框架设计师眼中的Thread机制   

      基于这个Runnable接口,Thread基类就能诞生一个新线程,并透过Runnable接口去调用子类别的run()函数。如下图所示: 

 

  图1-21  Thread框架的元素互动情形

      这个Thread类别扮演引擎角色,Runnable是轮盘接口,而Task则是轮胎。在Thread的start()函数会诞生一个小线程,反向调用Task的run()函数;必要时,Task会正向调用Thread的currentThread()等函数。

    换句话说,Runnable扮演<I>的角色,Thread扮演<E>的角色,而Task扮演<T>的角色。Thread引擎透过Runnable接口,调用了Task的run()等函数。我们称之为{Thread, Runnable, <T>}造形,或称为:Thread框架造形。[歡迎光臨 高煥堂 網頁:http://www.cnblogs.com/myEIT/ ]

两个造形的组合

    将上述的Thread框架造形,与Android的其它框架造形,组合在一起,就形成一支可用的应用程序了。如下图:

  

  图1-22  Thread造形与Activity造形的美丽组合

       接下来,还可以调整一下结构,将两个应用子类合并起来。这是把run()函数写进Activity的子类别里。于是,可促成Thread造形与Activity造形的新组合,如下图所示:

 

 图1-23  Activity造形与Thread造形的新组合

     由于在这个程序只会诞生myActivity对象,却可能诞生多个Thread对象,可能出现多条线程同时并行(Concurrently)执行run()函数的情形。此时必须特别留意线程冲突问题。也就是多条线程共享变量或对象,导致互相干扰计算中的变量值,因而产生错误的计算结果。例如,依据上图的设计结构,撰写程序码,可能无意中这会产生冲突了,如下范例:

 

// myActivity.java (Ex01_04)

package com.misoo.pk002;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

 

public class myActivity extends Activity implements OnClickListener, Runnable {

        private Button ibtn;

        private int sum;

       

    @Override

    protected void onCreate(Bundle icicle) {

        super.onCreate(icicle);

        LinearLayout layout = new LinearLayout(this);

        layout.setOrientation(LinearLayout.VERTICAL);

        ibtn = new Button(this);  ibtn.setOnClickListener(this);

        ibtn.setText("Exit");    ibtn.setBackgroundResource(R.drawable.gray);

        LinearLayout.LayoutParams param1 =

              new LinearLayout.LayoutParams(150, 55);

                    param1.topMargin = 10;

                    param1.leftMargin = 10;

        layout.addView(ibtn, param1);

        setContentView(layout);

        //-----------------------------------------------

        Thread th1 = new Thread(this);    th1.start();

        Thread th2 = new Thread(this);    th2.start();

        try {   Thread.sleep(1000);

                } catch (InterruptedException e) {       e.printStackTrace();  }

        setTitle(String.valueOf(sum));

    }

    public void onClick(View v) {    finish();    }

    //------------------------------------------

    @Override public void run() {

                     sum = 0;

                     for(int i=0; i<10000; i++ )

                     sum += 1;

        }

}

 

    第一个线程还没做完run()函数的计算,其后的第二个线程就进来run()函数,并行共享了sum变量值,因而输出错误的结果:11373。 

  

此时,可以使用synchronized机制来错开两个线程,就正确了。例如将上数程序码修改如下:

 

// …………

int sum;    

Thread th1 = new Thread(this);

th1.start();

Thread th2 = new Thread(this);

th2.start();

Thread.sleep(1000);

setTitle(String.valueOf(sum));

// ………….

@Override public void run() {

          this.exec();

}

public synchronized void exec(){

      sum = 0;

for(int i=0; i<10000; i++ )

                  sum += 1;

      }

// end

 

    第二个线程会等待第一个线程离开exec()函数之后才能进入exec(),就不会产生共享sum变量值的现象了。由于变量就存于对象内部,如果不共享对象,就可避免共享内部变量的问题。

于是,我们可以改变组合,将上述图1-23的Thread造形,与Activity造形组合起来,就可以避免共享对象的问题;如下图所示: 

 

   图1-24  避免了多线程共享对象问题

      依据此图,将上述Ex01_04的程序码修改如下:

 

      //…………………

      int sum;

        myThread th1 = new myThread();

        th1.start();

        myThread th2 = new myThread();

        th2.start();

        Thread.sleep(1000);

            setTitle(String.valueOf(th1.sum));

          //………………….

            class myThread extends Thread{

                public int sum;

 

                @Override

            public void run() {

                 sum = 0;

                     for(int i=0; i<10000; i++ )

                     sum += 1;

        }

    }

}

// end

 

    两个线程各使用自己的对象,也就是各自使用自己的sum变量值,就没有线程冲突问题了。

 

[Go Back]