这篇文章描述两种线程共享实例变量的通用模型,一个Java线程是Thread类的实例,一个thread必须从一个thread base创建。一个thread包含一个thread base和隐藏的控制结构,隐藏的控制结构允许thread可以与其他线程并行执行。
每个Java应用至少包含两个线程,一个是主线程,另一个是垃圾回收线程。
thread base是以下一个class的实例
thread base必须拥有下面的方法
public void run();
第一个thread base必须定义run方法,是为了implements Runnable接口。第一个thread base是Thread类本身作为thread base,提供run方法的简单定义,继承这个类的class必须覆写这个简单定义。
只要线程t从thread base中创建,通过执行start()方法来启动。
假如有一个implements Runnable的ThreadBase,主方法可以创建一个thread base:
ThreadBase base = new ThreadBase();
线程可以从同一个thread base中创建
Thread A = new Thread(base, "A");
Thread B = new Thread(base, "B");
执行start()方法调用
A.start();
B.start();
A和B共享thread base的成员变量,A和B拥有相同的run方法,但是他们的线程名字是不同的,仅仅成员变量可以共享,run方法使用的本地变量并不会共享。
假设除了main类还有三个类
主方法创建一个对象,这个对象的成员变量要被线程共享。
Shared s = new Shared();
创建线程
ThreadA A = new ThreadA(s);
ThreadB B = new ThreadB(s);
线程A和B使用不同的thread bases创建,A和B拥有不同的thread base。
线程A和B通过构造函数传递对象s,线程A如下:
class ThreadA extends Thread
{
ThreadA( Shared s ) { this.s = s; } // set member = parameter
private Shared s;
...
}
在线程A中的s成员变量指向原对象s。
两种模型的区别
在Java中的每个对象都有一个内部lock(有一个locked变量)和一个阻塞线程的队列,互斥锁代码块可以通过synchronized关键字创建
Object x = new Object();
synchronized(x)
{
// mutual exclusive access for all threads which share x
}
一个线程仅仅能够运行在synchronized(x)区域,如果它获得这个x锁。否则如果它尝试运行,将会阻塞。当一个线程离开这个区域,将会唤醒一个阻塞的线程。
另一种方法创建互斥锁的方法是
class MyClass {
public synchronized void foo() {
// ...
}
}
在这种情况下,如果多个线程共享对象
MyClass c = new MyClass();
所有对c.foo()的调用都是互斥的。在MyClass类中对foo()方法的调用实际是this.foo(),在这种情况,this的lock被使用。
假如两个线程共享integer counter,对counter的访问是在cs()方法中,我们想要确保在cs()方法的互斥。
class Main
{
public static void main(String args[])
{
ThreadBase base = new ThreadBase();
Thread A = new Thread( base, "A" );
Thread B = new Thread( base, "B" );
A.start(); B.start();
// ...
}
}
class ThreadBase implements Runnable
{
int counter = 0;
public void run()
{
while (true) {
/* non-critical section */
cs();
}
}
private void cs() {
int x = counter; // read into non-shared variable
x = x + 1; // update (this could be more elaborate)
counter = x; // write
}
}
线程A和B共享base对象。base对象是最适合添加lock的选择,有两种方法添加锁,后一种方法更好
private void cs() {
synchronized(this) {
/* body of cs() */
}
}
private synchronized void cs() {
/* body of cs() */
}
class Main
{
public static void main(String args[])
{
Counter c = new Counter();
Thread A = new ThreadA( c );
Thread B = new ThreadB( c );
A.start(); B.start();
// ...
}
}
class Counter
{
int val = 0; // for simplicity, val is not private
}
class ThreadA extends Thread
{
private Counter c;
ThreadA( Counter c ) { this.c = c; }
public void run()
{
while (true) {
/* non-critical section */
cs();
}
}
private void cs() {
int x = c.val; // read into non-shared variable
x = x + 1; // update (this could be more elaborate)
c.val = x; // write
}
}
class ThreadB extends Thread
{
private Counter c;
ThreadB( Counter c ) { this.c = c; }
// everything else is identical to ThreadA
}
c对象是最适合添加锁的选择,仅仅有一种方式
private void cs()
{
synchronized(c) {
/* body of cs() */
}
}
class Counter
{
private int val = 0; // now we can make val private
public synchronized void cs() {
int x = val;
x = x + 1;
val = x;
}
}
在这种情况下,cs()不是线程A和线程B的成员方法
public void run()
{
while (true) {
/* non-critical section */
c.cs(); // call cs() through Counter c
}
}