2006-10-27
scjp学习笔记之线程
关键字: scjp 学习笔记 线程
重中之重,线程部分。多线程的程序写的少,一般都是单线程的。在线程方面训练不够,复习的仔细点。
1、线程的三个部分
处理机
代码
数据
2、代码可以或不可以由多个线程共享,这和数据是独立的。两个线程如果执行同一个类的实例代码,则它们可以共享相同的代码。
类似地,数据可以或不可以由多个线程共享,这和代码是独立的。两个线程如果共享对一个公共对象的存取,则它们可以共享相同的数据。
在Java编程中,虚拟处理机封装在Thread类的一个实例里。构造线程时,定义其上下文的代码和数据是由传递给它的构造函数的对象指定的。
3、创建线程
多线程编程
从同一个Runnbale实例派生的多线程
线程共享数据和代码。
4、一个Thread类构造函数带有一个参数,它是Runnable的一个实例。一个Runnable是由一个实现了Runnable接口(即,提供了一个public void run()方法)的类产生的。
例如:
首先,main()方法构造了Xyz类的一个实例r。实例r有它自己的数据,在这里就是整数i。因为实例r是传给Thread的类构造函数的,所以r的整数i就是线程运行时刻所操作的数据。线程总是从它所装载的Runnable实例(在本例中,这个实例就是r。)的run()方法开始运行。
一个多线程编程环境允许创建基于同一个Runnable实例的多个线程。这可以通过以下方法来做到:
Thread t1= new Thread(r);
Thread t2= new Thread(r);
此时,这两个线程共享数据和代码。
5、启动线程
使用start()方法
使线程置于可运行状态
一个新创建的线程并不自动开始运行。你必须调用它的start()方法。调用start()方法使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。
6、尽管线程变为可运行的,但它并不立即开始运行。在一个只带有一个
处理机的机器上,在一个时刻只能进行一个动作。下节描述了如果有一个以上可运行线程时,如何分配处理机。
在Java中,线程是抢占式的,但并不一定是分时的 (一个常见的错误是认为“抢占式”只不过是“分时”的一种新奇的称呼而已) 。
抢占式调度模型是指可能有多个线程是可运行的,但只有一个线程在实际运行。这个线程会一直运行,直至它不再是可运行的,或者另一个具有更高优先级的线程成为可运行的。对于后面一种情形,低优先级线程被高优先级线程抢占了运行的机会。
一个线程可能因为各种原因而不再是可运行的。线程的代码可能执行了一个Thread.sleep()调用,要求这个线程暂停一段固定的时间。这个线程可能在等待访问某个资源,而且在这个资源可访问之前,这个线程无法继续运行。
所有可运行线程根据优先级保存在池中。当一个被阻塞的线程变成可运行时,它会被放回相应的可运行池。优先级最高的非空池中的线程会得到处理机时间(被运行)。
因为Java线程不一定是分时的,所有你必须确保你的代码中的线程会不时地给另外一个线程运行的机会。这可以通过在各种时间间隔中发出sleep()调用来做到。
注意try和catch块的使用。Thread.sleep()和其它使线程暂停一段时间的方法是可中断的。线程可以调用另外一个线程的interrupt()方法,这将向暂停的线程发出一个InterruptedException。
注意Thread类的sleep()方法对当前线程操作,因此被称作Thread.sleep(x),它是一个静态方法。sleep()的参数指定以毫秒为单位的线程最小休眠时间。除非线程因为中断而提早恢复执行,否则它不会在这段时间之前恢复执行。
Thread类的另一个方法yield(),可以用来使具有相同优先级的线程获得执行的机会。如果具有相同优先级的其它线程是可运行的,yield()将把调用线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行进程,yield()什么都不做。
注意sleep()调用会给较低优先级线程一个运行的机会。yield()方法只会给相同优先级线程一个执行的机会。
7、线程的基本控制
终止一个线程
8、在一段特定的代码中,可以使用静态Thread方法currentThread()来获取对当前线程的引用。Thread.currentThread();
9、测试一个线程
isAlive()
有时线程可处于一个未知的状态。isAlive()方法用来确定一个线程是否仍是活的。活着的线程并不意味着线程正在运行;对于一个已开始运行但还没有完成任务的线程,这个方法返回true。
10、延迟线程
存在可以使线程暂停执行的机制。也可以恢复运行,就好象什么也每发生过一样,线程看上去就象在很慢地执行一条指令。
sleep()
sleep()方法是使线程停止一段时间的方法。在sleep时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非
(a)“醒来”的线程具有更高的优先级
(b)正在运行的线程因为其它原因而阻塞
join()
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。例如:
可以带有一个以毫秒为单位的时间值来调用join方法,例如:
void join (long timeout);
其中join()方法会挂起当前线程。挂起的时间或者为timeout毫秒,或者挂起当前线程直至它所调用的线程终止。
12、创建线程的其它方法
到目前为止,你已经知道如何用实现了Runnable的分离类来创建线程上下文。事实上,这不是唯一的方法。Thread类自身实现了Runnable接口,所以可以通过扩展Thread类而不是实现Runnable来创建线程。
使用那种方法?
1)实现Runnable
更符合面向对象的设计:
从面向对象的角度来看,Thread类是一个虚拟处理机严格的封装,因此只有当处理机模型修改或扩展时,才应该继承类。正因为这个原因和区别一个正在运行的线程的处理机、代码和数据部分的意义,本教程采用了这种方法。
单继承:
由于Java技术只允许单一继承,所以如果你已经继承了Thread,你就不能再继承其它任何类,例如Applet。在某些情况下,这会使你只能采用实现Runnable的方法。
一致性:
因为有时你必须实现Runnable,所以你可能喜欢保持一致,并总是使用这种方法。
2)扩展Thread:
当一个run()方法体现在继承Thread类的类中,用this指向实际控制运行的Thread实例。因此,代码不再需要使用如下控制:
Thread.currentThread().join();
而可以简单地用:
join();
因为代码简单了一些,许多Java编程语言的程序员使用扩展Thread的机制。注意:如果你采用这种方法,在你的代码生命周期的后期,单继承模型可能会给你带来困难。
所以,还是继承runnable方法好。
关键字synchronized
它提供Java编程语言一种机制,允许程序员控制共享数据的线程。
想象一个表示栈的类。这个类最初可能象下面那样:
13、对象锁标志
每个对象都有一个标志,它可以被认为是“锁标志”。
synchronized允许和锁标志交互。
在Java技术中,每个对象都有一个和它相关联的标志。这个标志可以被认为是“锁标志”。 synchronized关键字使能和这个标志的交互,即允许独占地存取对象。看一看下面修改过的代码片断:
public void push(char c) {
synchronized(this) {
data[idx] = c;
idx++;
}
}
当线程运行到synchronized语句,它检查作为参数传递的对象,并在继续执行之前试图从对象获得锁标志。
意识到它自身并没有保护数据是很重要的。因为如果同一个对象的pop()方法没有受到synchronized的影响,且pop()是由另一个线程调用的,那么仍然存在破坏data的一致性的危险。如果要使锁有效,所有存取共享数据的方法必须在同一把锁上同步。
如果pop()受到synchronized的影响,且另一个线程在原线程持有那个对象的锁时试图执行pop()方法时所发生的事情:
当线程试图执行synchronized(this)语句时,它试图从this对象获取锁标志。由于得不到标志,所以线程不能继续运行。然后,线程加入到与那个对象锁相关联的等待线程池中。当标志返回给对象时,某个等待这个标志的线程将得到这把锁并继续运行。
14、释放锁标志
线程执行到synchronized()代码块末尾时释放
synchronized()代码块抛出中断或异常时自动释放
此外,如果一个线程对同一个对象两次发出synchronized调用,则在跳出最外层的块时,标志会正确地释放,而最内层的将被忽略。
1、线程的三个部分
处理机
代码
数据
2、代码可以或不可以由多个线程共享,这和数据是独立的。两个线程如果执行同一个类的实例代码,则它们可以共享相同的代码。
类似地,数据可以或不可以由多个线程共享,这和代码是独立的。两个线程如果共享对一个公共对象的存取,则它们可以共享相同的数据。
在Java编程中,虚拟处理机封装在Thread类的一个实例里。构造线程时,定义其上下文的代码和数据是由传递给它的构造函数的对象指定的。
3、创建线程
多线程编程
从同一个Runnbale实例派生的多线程
线程共享数据和代码。
4、一个Thread类构造函数带有一个参数,它是Runnable的一个实例。一个Runnable是由一个实现了Runnable接口(即,提供了一个public void run()方法)的类产生的。
例如:
1.public class ThreadTest {
2.public static void main(String args[]) {
3.Xyz r = new Xyz();
4.Thread t = new Thread(r);
5.t.start();}
6.}
7.
8.class Xyz implements Runnable {
9.int i;
10.
11.public void run() {
12.while (true) {
13.System.out.println("Hello " + i++);
14.if (i == 50) break;
15.}
16.}
17.}
首先,main()方法构造了Xyz类的一个实例r。实例r有它自己的数据,在这里就是整数i。因为实例r是传给Thread的类构造函数的,所以r的整数i就是线程运行时刻所操作的数据。线程总是从它所装载的Runnable实例(在本例中,这个实例就是r。)的run()方法开始运行。
一个多线程编程环境允许创建基于同一个Runnable实例的多个线程。这可以通过以下方法来做到:
Thread t1= new Thread(r);
Thread t2= new Thread(r);
此时,这两个线程共享数据和代码。
5、启动线程
使用start()方法
使线程置于可运行状态
一个新创建的线程并不自动开始运行。你必须调用它的start()方法。调用start()方法使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。
6、尽管线程变为可运行的,但它并不立即开始运行。在一个只带有一个
处理机的机器上,在一个时刻只能进行一个动作。下节描述了如果有一个以上可运行线程时,如何分配处理机。
在Java中,线程是抢占式的,但并不一定是分时的 (一个常见的错误是认为“抢占式”只不过是“分时”的一种新奇的称呼而已) 。
抢占式调度模型是指可能有多个线程是可运行的,但只有一个线程在实际运行。这个线程会一直运行,直至它不再是可运行的,或者另一个具有更高优先级的线程成为可运行的。对于后面一种情形,低优先级线程被高优先级线程抢占了运行的机会。
一个线程可能因为各种原因而不再是可运行的。线程的代码可能执行了一个Thread.sleep()调用,要求这个线程暂停一段固定的时间。这个线程可能在等待访问某个资源,而且在这个资源可访问之前,这个线程无法继续运行。
所有可运行线程根据优先级保存在池中。当一个被阻塞的线程变成可运行时,它会被放回相应的可运行池。优先级最高的非空池中的线程会得到处理机时间(被运行)。
因为Java线程不一定是分时的,所有你必须确保你的代码中的线程会不时地给另外一个线程运行的机会。这可以通过在各种时间间隔中发出sleep()调用来做到。
1.public class Xyz implements Runnable {
2.public void run() {
3.while (true) {
4.// do lots of interesting stuff
5.:
6.// Give other threads a chance
7.try {
8.Thread.sleep(10);
9.} catch (InterruptedException e) {
10.// This thread's sleep was interrupted
11.// by another thread
12.}
13.}
14.}
15.}
注意try和catch块的使用。Thread.sleep()和其它使线程暂停一段时间的方法是可中断的。线程可以调用另外一个线程的interrupt()方法,这将向暂停的线程发出一个InterruptedException。
注意Thread类的sleep()方法对当前线程操作,因此被称作Thread.sleep(x),它是一个静态方法。sleep()的参数指定以毫秒为单位的线程最小休眠时间。除非线程因为中断而提早恢复执行,否则它不会在这段时间之前恢复执行。
Thread类的另一个方法yield(),可以用来使具有相同优先级的线程获得执行的机会。如果具有相同优先级的其它线程是可运行的,yield()将把调用线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行进程,yield()什么都不做。
注意sleep()调用会给较低优先级线程一个运行的机会。yield()方法只会给相同优先级线程一个执行的机会。
7、线程的基本控制
终止一个线程
8、在一段特定的代码中,可以使用静态Thread方法currentThread()来获取对当前线程的引用。Thread.currentThread();
9、测试一个线程
isAlive()
有时线程可处于一个未知的状态。isAlive()方法用来确定一个线程是否仍是活的。活着的线程并不意味着线程正在运行;对于一个已开始运行但还没有完成任务的线程,这个方法返回true。
10、延迟线程
存在可以使线程暂停执行的机制。也可以恢复运行,就好象什么也每发生过一样,线程看上去就象在很慢地执行一条指令。
sleep()
sleep()方法是使线程停止一段时间的方法。在sleep时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非
(a)“醒来”的线程具有更高的优先级
(b)正在运行的线程因为其它原因而阻塞
join()
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。例如:
public void doTask() {
TimerThread tt = new TimerThread (100);
tt.start ();
...
// Do stuff in parallel with the other thread for
// a while
...
// Wait here for the timer thread to finish
try {
tt.join ();
} catch (InterruptedException e) {
// tt came back early
}
...
// Now continue in this thread
...
}
可以带有一个以毫秒为单位的时间值来调用join方法,例如:
void join (long timeout);
其中join()方法会挂起当前线程。挂起的时间或者为timeout毫秒,或者挂起当前线程直至它所调用的线程终止。
12、创建线程的其它方法
到目前为止,你已经知道如何用实现了Runnable的分离类来创建线程上下文。事实上,这不是唯一的方法。Thread类自身实现了Runnable接口,所以可以通过扩展Thread类而不是实现Runnable来创建线程。
1.public class MyThread extends Thread {
2.public void run() {
3.while (running) {
4.// do lots of interesting stuff
5.try {
6.sleep(100);
7.} catch (InterruptedException e) {
8.// sleep interrupted
9.}
10.}
11.}
12.
13.public static void main(String args[]) {
14.Thread t = new MyThread();
15.t.start();
16.}
17.}
使用那种方法?
1)实现Runnable
更符合面向对象的设计:
从面向对象的角度来看,Thread类是一个虚拟处理机严格的封装,因此只有当处理机模型修改或扩展时,才应该继承类。正因为这个原因和区别一个正在运行的线程的处理机、代码和数据部分的意义,本教程采用了这种方法。
单继承:
由于Java技术只允许单一继承,所以如果你已经继承了Thread,你就不能再继承其它任何类,例如Applet。在某些情况下,这会使你只能采用实现Runnable的方法。
一致性:
因为有时你必须实现Runnable,所以你可能喜欢保持一致,并总是使用这种方法。
2)扩展Thread:
当一个run()方法体现在继承Thread类的类中,用this指向实际控制运行的Thread实例。因此,代码不再需要使用如下控制:
Thread.currentThread().join();
而可以简单地用:
join();
因为代码简单了一些,许多Java编程语言的程序员使用扩展Thread的机制。注意:如果你采用这种方法,在你的代码生命周期的后期,单继承模型可能会给你带来困难。
所以,还是继承runnable方法好。
关键字synchronized
它提供Java编程语言一种机制,允许程序员控制共享数据的线程。
想象一个表示栈的类。这个类最初可能象下面那样:
1.public class MyStack {
2.
3.int idx = 0;
4.char [] data = new char[6];
5.
6.public void push(char c) {
7.data[idx] = c;
8.idx++;
9.}
10.
11.public char pop() {
12.idx--;
13.return data[idx];
14.}
15.}
13、对象锁标志
每个对象都有一个标志,它可以被认为是“锁标志”。
synchronized允许和锁标志交互。
在Java技术中,每个对象都有一个和它相关联的标志。这个标志可以被认为是“锁标志”。 synchronized关键字使能和这个标志的交互,即允许独占地存取对象。看一看下面修改过的代码片断:
public void push(char c) {
synchronized(this) {
data[idx] = c;
idx++;
}
}
当线程运行到synchronized语句,它检查作为参数传递的对象,并在继续执行之前试图从对象获得锁标志。
意识到它自身并没有保护数据是很重要的。因为如果同一个对象的pop()方法没有受到synchronized的影响,且pop()是由另一个线程调用的,那么仍然存在破坏data的一致性的危险。如果要使锁有效,所有存取共享数据的方法必须在同一把锁上同步。
如果pop()受到synchronized的影响,且另一个线程在原线程持有那个对象的锁时试图执行pop()方法时所发生的事情:
当线程试图执行synchronized(this)语句时,它试图从this对象获取锁标志。由于得不到标志,所以线程不能继续运行。然后,线程加入到与那个对象锁相关联的等待线程池中。当标志返回给对象时,某个等待这个标志的线程将得到这把锁并继续运行。
14、释放锁标志
线程执行到synchronized()代码块末尾时释放
synchronized()代码块抛出中断或异常时自动释放
此外,如果一个线程对同一个对象两次发出synchronized调用,则在跳出最外层的块时,标志会正确地释放,而最内层的将被忽略。
发表评论
- 浏览: 28865 次
- 性别:

- 来自: 珠海

- 详细资料
搜索本博客
我的相册
codeGen
共 1 张
共 1 张
链接
最新评论
-
基于Ant+Velocity的简单代 ...
楼主的思想不错。建议多看看 RATIONAL ROSE 等根据设计而生成代码的工 ...
-- by sg552 -
基于Ant+Velocity的简单代 ...
可以用freemaker来做
-- by davexin -
基于Ant+Velocity的简单代 ...
呵呵,个人感觉不如使用类继承来解决代码重复,基类使用类名来进行规则处理。代码生成 ...
-- by cherami -
[Hibernae+Spring]压力测 ...
50个用户同时并发 ------------------ 请教楼主用什么测试软件 ...
-- by denghan -
[Hibernae+Spring]压力测 ...
Readonly 写道string类型PK改到数字类型PK只能减少page lo ...
-- by lzzzl






评论排行榜