1. 多线程 1.1 创建线程类 在Java中可以简单的从Thread类中继承创建自己的线程类: public class MyFirstThread extends Thread { public void run() { . . .} } 说明: (1) Thread类位是java.lang包中,所以可以不用显示import; (2) 从Thread类中继承下来的类最好重载run()方法,以运行需要的代码; 可以按以下方法实例化并运行线程: MyFirstThread aMFT = new MyFirstThread(); aMFT.start(); 说明: (3) 实例化线程类后,系统会初始化一些参数,主要是为线程创建名称,把新的线程加入指定的线程组,初始化线程运行需要的内存空间,指定新线程的优先级别,指定它的守候线程; (4) start方法是Thread类中的方法,它会调用run方法,在新的线程中运行指定的代码; (5) 除了start方法外,从Thread继承下来的类还具有其它一些主要的方法:stop,suspend,resume等; 以下是一个完整的Thread派生类: 1: public class ComplexThread extends Thread { 2: private int delay; 3: 4: ComplexThread(String name, float seconds) { 5: super(name); 6: delay = (int) seconds * 1000; // delays are in milliseconds 7: start(); // start up ourself! 8: } 9: 10: public void run() { 11: while (true) { 12: System.out.println(Thread.currentThread().getName()); 13: try { 14: Thread.sleep(delay); 15: } catch (InterruptedException e) { 16: return; 17: } 18: } 19: } 20: 21: public static void main(String argv[]) { 22: new ComplexThread("one potato", 1.1F); 23: new ComplexThread("two potato", 1.3F); 24: new ComplexThread("three potato", 0.5F); 25: new ComplexThread("four", 0.7F); 26: } 27: } 1.2 Runable接口 创建多线程运行指定代码的另一种方法是,在创建类时implement Runable这个接口: public class MySecondThread extends ImportantClass implements Runnable { public void run() {. . .} } 说明: (1) 该类implement Runable接口,就表明有意图运行在单独的线程中,Thread也是implement Runable接口的; (2) Implement Runalbe接口至少需要实现run方法; 以下是创建新线程运行该类的实例: MySecondThread aMST = new MySecondThread(); Thread aThread = new Thread(aMST); aThread.start(); 说明: (3) Thread类有多个构造函数Thread()、Thread(Runable target)等,本例中用的就是第二个构造函数,它有一个Runable类型的函数,所以要在多线程中运行的实例的类必须是implement Runable的; (4) AThead.start()方法调用Thread实例的中的target.run方法,本例中就是MySecondThread实例中的run方法; (5) Thread构造函数还可以指定线程名,运行所需的stack,线程所属的组等; 为了防止线程非正常结束,需要将start方法置入try…catch中,如: try{ myThread.start(); }catch(ThreadDeath aTD){ System.out.println("end Thread"); throw aTD; } 在这个例子中将捕获ThreadDeath异常,处理后重新抛出该异常,以便Java执行stop方法,进行资源等清理工作。 1.3 线程的优先级 多个线程的执行是有一定的优先级别的,对于下面这个例子: public class RunnablePotato implements Runnable { public void run() { while (true) System.out.println(Thread.currentThread().getName()); }} public class PotatoThreadTester { public static void main(String argv[]) { RunnablePotato aRP = new RunnablePotato(); Thread T1 = new Thread(aRP, "one potato"); Thread T2 = new Thread(aRP, "two potato"); T1.start(); T2.start(); }} 对于非抢占式的系统,上例中的第一个线程会一直运行,第二个线程没有机会运行;对于抢占式的系统,这二人线程会交替运行。 为了让多线程在非抢占式中运行,最好在run方法中加入以下语句: Thread.yield() 即 public void run() { while (true) System.out.println(Thread.currentThread().getName()); Thread.yield() } Thread.yield会将当前线程暂时让位一小段时间,让其它的线程有机会运行,过了这段时间后,该线程继承运行。上述功能也可以用Thread.sleep()方法实现。 在Java中有优先级别可以从1到10,其中1可以用Thread.MIN_PRIORITY表示,5可以用Thread.NORM_PRIORITY,10可以用Thread.MAX_PRIORITY表示,新建一个线程默认的级别是Thread.NORM_PRIORITY。也可以使用setPriority方法改变线程的优先级别,如T1.setPriority(T2.getPriority + 1)。 与yield方法相反的是join方法,它表示一直要等到指定的线程运行完毕,如: try{ t.join();} catch (InterruptedException ignored) { } 表示要等到线程t运行完毕后,再执行下一步操作。这种情况比较少见。 1.4 synchronized 为了保证某个方法或者对象某个时刻只能被一个方法访问,那就需要使用synchronized关键字。 如: public synchronized void countMe() { crucialValue += 1; } 就表示countMe这个方法中的操作是一个原子操作,+= 要执行三个步骤,使用synchronized后,这三个步骤是具有原子性,即在三个步骤完成前,其它对于crucialValue的访问都将被拒绝,即可保证countMe的线程安全。 另一个例子: synchronized(p) { safeX = p.x(); safeY = p.y(); } 表示在block范围内锁定p对象,不许其它程序修改p对象中的值。 以上代码的作用都是保护某个对象内的变量不能同时被多个线程访问,下面介绍如何保护class variable的线程安全: public class StaticCounter { private static int crucialValue; public void countMe() { synchronized(getClass()) { crucialValue += 1; } } } 说明: (1) 在这个例子中,crucialValue是private并且static,这表示它可以被该类的所有实例访问; (2) synchronized使用getClass方法获取类名,而不能直接使用StaticCounter (3) 如果crucialValue是public的,那么修改代码成: synchronized(Class.forName("StaticCounter")) { StaticCounter.crucialValue += 1; }
