| |
编写具有多线程能力的程序经常会用到的方法有:
run(), start(), wait(), notify(), notifyall(), sleep(), yield(), join()
还有一个重要的关键字:synchronized
本文将对以上内容进行讲解。
一:run() 和start()
示例1:
public class threadtest extends thread { public void run() { for (int i = 0; i < 10; i++) { system.out.print(" " + i); } }
public static void main(string[] args) { new threadtest().start(); new threadtest().start(); } }
这是个简单的多线程程序。run() 和start() 是大家都很熟悉的两个方法。把希望并行处理的代码都放在run() 中;stat() 用于自动调用run(), 这是java的内在机制规定的。并且run() 的访问控制符必须是public,返回值必须是void(这种说法不准确,run() 没有返回值),run() 不带参数。
这些规定想必大家都早已知道了,但你是否清楚为什么run方法必须声明成这样的形式?这涉及到java的方法覆盖和重载的规定。这些内容很重要, 请读者参考相关资料。
二:关键字synchronized
有了synchronized关键字,多线程程序的运行结果将变得可以控制。synchronized关键字用于保护共享数据。请大家注意 "共享数据", 你一定要分清哪些数据是共享数据,java是面向对象的程序设计语言,所以初学者在编写多线程程序时,容易分不清哪些数据是共享数据。请看下面的例子:
示例2:
public class threadtest implements runnable {
public synchronized void run() { for (int i = 0; i < 10; i++) { system.out.print(" " + i); } }
public static void main(string[] args) { runnable r1 = new threadtest(); runnable r2 = new threadtest(); thread t1 = new thread(r1); thread t2 = new thread(r2); t1.start(); t2.start(); } }
在这个程序中,run() 被加上了synchronized关键字。在main方法中创建了两个线程。你可能会认为此程序的运行结果一定为:0123456789 0123456789。但你错了!这个程序中synchronized关键字保护的不是共享数据( 其实在这个程序中synchronized关键字没有起到任何作用,此程序的运行结果是不可预先确定的)。这个程序中的t1, t2是两个对象(r1, r2)的线程。java是面向对象的程序设计语言,不同的对象的数据是不同的,r1, r2有各自的run() 方法,而synchronized使同一个对象的多个线程, 在某个时刻只有其中的一个线程可以访问这个对象的synchronized数据。每个对象都有一个 "锁标志", 当这个对象的一个线程访问这个对象的某个synchronized数据时,这个对象的所有被synchronized修饰的数据将被上锁(因为 "锁标志" 被当前线程拿走了),只有当前线程访问完它要访问的synchronized数据时,当前线程才会释放 "锁标志", 这样同一个对象的其它线程才有机会访问synchronized数据。
示例3:
public class threadtest implements runnable { public synchronized void run() { for (int i = 0; i < 10; i++) { system.out.print(" " + i); } }
public static void main(string[] args) { runnable r = new threadtest(); thread t1 = new thread(r); thread t2 = new thread(r); t1.start();
t2.start(); } }
如果你运行1000次这个程序,它的输出结果也一定每次都是:01234567890123456789。因为这里的synchronized保护的是共享数据。 t1, t2是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run() 方法时,由于run() 受synchronized保护,所以同一个对象的其他线程( t2)无法访问synchronized方法(run方法)。只有当t1执行完后t2才有机会执行。
示例4:
public class threadtest implements runnable { public void run() { synchronized (this) { for (int i = 0; i < 10; i++) { system.out.print(" " + i); } } }
public static void main(string[] args) { runnable r = new threadtest(); thread t1 = new thread(r); thread t2 = new thread(r); t1.start(); t2.start(); } }
这个程序与示例3的运行结果一样。在可能的情况下,应该把保护范围缩到最小,可以用示例4的形式,this代表 "这个对象"。没有必要把整个run() 保护起来, run() 中的代码只有一个for循环,所以只要保护for循环就可以了。
示例5:
public class threadtest implements runnable { public void run() { for (int k = 0; k < 5; k++) { system.out.println(thread.currentthread().getname() + " : for loop : " + k);
} synchronized (this) { for (int k = 0; k < 5; k++) { system.out.println(thread.currentthread().getname() + " : synchronized for loop : " + k); } } }
public static void main(string[] args) { runnable r = new threadtest(); thread t1 = new thread(r, "t1_name"); thread t2 = new thread(r, "t2_name"); t1.start(); t2.start(); } }
运行结果:t1_name : for loop : 0 t1_name : for loop : 1 t1_name : for loop : 2 t2_name : for loop : 0 t1_name : for loop : 3 t2_name : for loop : 1 t1_name : for loop : 4 t2_name : for loop : 2 t1_name : synchronized for loop : 0 t2_name : for loop : 3 t1_name : synchronized for loop : 1 t2_name : for loop : 4 t1_name : synchronized for loop : 2 t1_name : synchronized for loop : 3 t1_name : synchronized for loop : 4 t2_name : synchronized for loop : 0 t2_name : synchronized for loop : 1 t2_name : synchronized for loop : 2 t2_name : synchronized for loop : 3 t2_name : synchronized for loop : 4
第一个for循环没有受synchronized保护。对于第一个for循环,t1, t2可以同时访问。运行结果表明t1执行到了k = 2时,t2开始执行了。t1首先执行完了第一个for循环,此时还没有执行完第一个for循环( t2刚执行到k = 2)。t1开始执行第二个for循环,当t1的第二个for循环执行到k = 1时,t2的第一个for循环执行完了。 t2想开始执行第二个for循环,但由于t1首先执行了第二个for循环,这个对象的锁标志自然在t1手中( synchronized方法的执行权也就落到了t1手中),在t1没执行完第二个for循环的时候,它是不会释放锁标志的。 所以t2必须等到t1执行完第二个for循环后,它才可以执行第二个for循环
三:sleep()
示例6:
public class threadtest implements runnable { public void run() { for (int k = 0; k < 5; k++) { if (k == 2) { try { thread.currentthread().sleep(5000); } catch (exception e) {} } system.out.print(" " + k); } }
public static void main(string[] args) { runnable r = new threadtest(); thread t = new thread(r); t.start(); } }
sleep方法会使当前的线程暂停执行一定时间(给其它线程运行机会)。读者可以运行示例6,看看结果就明白了。sleep方法会抛出异常,必须提供捕获代码。
示例7:
public class threadtest implements runnable { public void run() {
for (int k = 0; k < 5; k++) { if (k == 2) { try { thread.currentthread().sleep(5000); } catch (exception e) {} } system.out.println(thread.currentthread().getname() + " : " + k); } }
福建JSP技术网
--> |
|