目录
7、 线程安全的化解之例
复习:Android单线程环境
非单线程环境的线程安全议题
范例-1
范例-2编辑
同步(Synchronization)化解线程安全的问题
7、 线程安全的化解之例
复习:Android单线程环境
- View是一个单线程的类;其意味着:此类的撰写着心中意图只让有一个线程来执行这个类的代码(如函数调用)。
- 由于View类开发者心怀<单线程>,则View类的Client开发者就不宜让多线程去执行View的代码。
// ac01.java
// ……..
public class ac01 extends Activity implements OnClickListener {
private Button btn;
public void onCreate(Bundle icicle) {
// ……..
btn = new Button(this);
btn.setText(“Exit");
// ……..
}
public void f1() {
// ……..
btn.setText(“OK");
// ……..
}
}
- 同样地, View的子类开发者也不宜让多线程去执行View(基类)的代码。
/* ---- ac01.java ---- */ // …….. public class ac01 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); okButton ok_btn = new okButton(this); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(ok_btn.get_width(), ok_btn.get_height()); // …….. } } /* ---- okButton ---- */ // ………. public class okButton extends Button{ public okButton(Context ctx){ super(ctx); super.setText("OK"); super.setBackgroundResource(R.drawable.ok_blue); } public void f1() { super.setText("Quit"); } public int get_width(){ return 90; } public int get_height(){ return 50; } }
非单线程环境的线程安全议题
- 如果共享对象或变量是不可避免的话,就得试图错开线程的执行时刻了。
- 由于共享对象或变量,若两个线程会争相更改对象的属性值或变量值时,则可能会互相干扰对方的计算过程和结果。 例如:
范例-1
class Task implements Runnable {
private int count;
public void init(){ count = 0; }
public void f1() {
for(int i=0; i<3; i++) {
count++;
try {
Thread.sleep(10);
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() +"'s count: " + count);
}
}
public void run() {
this.init();
this.f1();
}
}
public class JMain {
public static void main(String[] args) {
Task ta = new Task();
Thread t1 = new Thread( ta, "A");
Thread t2 = new Thread( ta, "B");
t1.start();
t2.start();
System.out.println("Waiting...");
}
}
范例-2
- 由于在这个程序只会诞生myActivity对象,却可能诞生多个Thread对象,可能出现多条线程同时并行(Concurrently)执行run()函数的情形。此时必须特别留意线程冲突问题。也就是多条线程共享变量或对象,导致互相干扰计算中的变量值,因而产生错误的计算结果。
- 例如,依据上图的设计结构,撰写程序码,可能无意中这会产生冲突了。
- 如下范例:
// myActivity.java
//……….
public class myActivity extends Activity implements OnClickListener, Runnable {
private Button ibtn;
private int sum;
@Override
protected void onCreate(Bundle icicle) {
//………
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。
同步(Synchronization)化解线程安全的问题
- 此时,可以使用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变量值的现象了。由于变量就存于对象内部,如果不共享对象,就可避免共享内部变量的问题。