JAVA
多线程系统建立于
Thread
类,它的方法,它的共伴接口
Runnable
基础上,
Thread
类封装了多线程的执行。为创建一个新的线程,你的程序必须实现
Runnabel
接口或继承
Thread
类。
Thread
类定义了好几个方法来帮助管理线程,本章用到的方法表如下:
方法
|
意义
|
getName
|
获得线程名
|
getPrionty
|
获得线程优先级
|
jsAlive
|
判定线程是否仍运行
|
Join
|
等侍一个线程终止
|
Run
|
线程的入口点
|
Sleep
|
在一段时候时挂起线程
|
Start
|
通过调用运行的方法来启动线程
|
主线程
任何一个
JAVA
都有一个主线程,当
JAVA
程序启动时一个线程立刻运行,该线程通常叫做程序的主线程。因为它是程序开始执行时就运行,主线程的重要性体现在:
A、
通过它产生期它子线程
B、
通常它必须最后完成执行,因为它执行各种关闭动作。
尽管主线程在程序启动时自动创建,但它可以由一个
Thread
对象控制。为此,必须调用
currentThread()
获得它的一个引用,
currentThread
是
Thread
类的公有的静态成员:
static Thread currentThread();
该方法返回一个调用它的线程的引用。一旦获得主线程的引用,就可以像控制其它线程那样控制主线程。
package com.jdk.thread;
public class CurrentThreadDemo1 {
/**
* @param args
*/
public static void main(String[] args) {
Thread t=Thread.currentThread();
System.out.println("current thread->"+t);
t.setName("main thread");
System.out.println("change after thread name->"+t);
try{
for(int i=5;i>0;i--){
System.out.println("i-->"+i);
t.sleep(1000);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
以上代码是一个最简单的
Thread
方法例子。
创建线程最简单的办法就是实现
Runnable
接口的类,
Runnable
操象了一个执行代码单元。为实现
Runnable
接口,一个类仅需实现
Run()
的简单方法,该方法声明如下:
public void run();
在
run
中可以定义代码来构建新的线程,
run
方法能够像主线程那样调用其它方法,引用其它类,声明变量。仅有的不同是
run()
在程序中确立另一个并发程序的入口。当
run
返回时,该线程结束。这是一个例子程序:
package com.jdk.thread;
class newThread implements Runnable{
Thread t;
public newThread(){
t=new Thread(this,"Demo Thread");
System.out.println("Client Thread ->"+t);
t.start();
}
public void run() {
try{
for(int i=5;i>0;i--){
System.out.println("Client Thread->"+i);
Thread.sleep(500);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println("
子线程结束
");
}
}
public class CurrentThreadDemo2 {
public static void main(String[] args) {
new newThread();
try{
for(int i=5;i>0;i--){
System.out.println("main Thread ->"+i);
Thread.sleep(1000);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println("
主线程结束
");
}
}
通过该例子,我们应该知道
run()
方法是一个多线程的并发入口和结束方法,支持多线程的类必须要实现
Runnable
接口或继承
Thread
类。通常主线程必须是结束运行的最后一个线程(旧版
JVM
)。通常在支持多线程的类的构造函数中创建并启动线程,创建线程就生成一个实现了
Runnable
接口的类,启动线程就是调用该类的
start()
方法作用是运行
run()
方法。
下面是一个创建多线程的类:
package com.jdk.thread;
class ThreadDemo3 implements Runnable{
String threadName;
Thread t;
public ThreadDemo3(String tname){
this.threadName=tname;
t=new Thread(this,threadName);
t.start();
}
public void run() {
try{
for(int i=5;i>0;i--){
System.out.println(threadName+"-->"+i);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println(threadName+"
结束
!");
}
}
public class Thread3 {
/**
* @param args
*/
public static void main(String[] args) {
ThreadDemo3 td1=new ThreadDemo3("one");
ThreadDemo3 td2=new ThreadDemo3("two");
ThreadDemo3 td3=new ThreadDemo3("three");
System.out.println("
主线程结束
!");
}
}
输出结果是:
主线程结束
!
one-->5
one-->4
one-->3
one-->2
one-->1
one
结束
!
two-->5
two-->4
two-->3
two-->2
two-->1
two
结束
!
three-->5
three-->4
three-->3
three-->2
three-->1
three
结束
!
当把上代码改成这样:
package com.jdk.thread;
class ThreadDemo3 implements Runnable{
String threadName;
Thread t;
public ThreadDemo3(String tname){
this.threadName=tname;
t=new Thread(this,threadName);
t.start();
}
public void run() {
try{
for(int i=5;i>0;i--){
System.out.println(threadName+"-->"+i);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println(threadName+"
结束
!");
}
}
public class Thread3 {
/**
* @param args
*/
public static void main(String[] args) {
ThreadDemo3 td1=new ThreadDemo3("one");
ThreadDemo3 td2=new ThreadDemo3("two");
ThreadDemo3 td3=new ThreadDemo3("three");
try{
Thread t=Thread.currentThread();
t.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("
主线程结束
!");
}
}
结果如下:
one-->5
one-->4
one-->3
one-->2
one-->1
one
结束
!
two-->5
two-->4
two-->3
two-->2
two-->1
two
结束
!
three-->5
three-->4
three-->3
three-->2
three-->1
three
结束
!
主线程结束
!
使用
isAlive
和
join
通过前面的例子可以看出我们是使用
sleep
()方法来延时,等待子线程结束,它带来了一个更大的问题是一个线程怎么知道另一个线程已经结束呢?通常有两种方法可以解决:
1、
在线程中调用
isAlive
(),这种方法由
Thread
定义,通常形式如下:
final Boolean isAlive();
如果调用的线程还在运行则返回
True
,否则返回
False;
但
isAlive
很少用到,等待线程结束通常使用的方法是调用
join()
。
2、
join()
形式如下:
final void join()throw InterruptedException
该方法等侍所调用的线程结束,该名字来自于要求线程等待直到指定的线程参与的概念。下面的例子是前面例子的改进,演示用
join()
以确保主线程最后结束。可样也演示了
isAlive()
方法,代码如下:
package com.jdk.thread;
class ThreadDemo3 implements Runnable{
String threadName;
Thread t;
public ThreadDemo3(String tname){
this.threadName=tname;
t=new Thread(this,threadName);
t.start();
}
public void run() {
try{
for(int i=5;i>0;i--){
System.out.println(threadName+"-->"+i);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println(threadName+"
结束
!");
}
}
public class Thread3 {
public static void main(String[] args) {
ThreadDemo3 td1=new ThreadDemo3("one");
ThreadDemo3 td2=new ThreadDemo3("two");
ThreadDemo3 td3=new ThreadDemo3("three");
System.out.println("td1
状态
-->"+td1.t.isAlive());
System.out.println("td2
状态
-->"+td2.t.isAlive());
System.out.println("td3
状态
-->"+td3.t.isAlive());
try{
System.out.println("
等待子线程结束
!");
td1.t.join();
td2.t.join();
td3.t.join();
}catch(Exception e){
e.printStackTrace();
}
System.out.println("td1
状态
-->"+td1.t.isAlive());
System.out.println("td2
状态
-->"+td2.t.isAlive());
System.out.println("td3
状态
-->"+td3.t.isAlive());
System.out.println("
主线程结束
!");
}
}
输出的结果如下:
td1
状态
-->true
td2
状态
-->true
td3
状态
-->true
等待子线程结束
!
one-->5
one-->4
one-->3
one-->2
one-->1
one
结束
!
two-->5
two-->4
two-->3
two-->2
two-->1
two
结束
!
three-->5
three-->4
three-->3
three-->2
three-->1
three
结束
!
td1
状态
-->false
td2
状态
-->false
td3
状态
-->false
主线程结束
!
线程的优先级别
通常优先级别高的线程比优先级别低的线程获得更多
CPU
时间,但实际与这与很多因素有关。就不要管这么多了,懂怎么设置线程的优选级别就
OK
。设置线程优选级别的方法如下:
final void setPriority(int level) ;
该方法是
Thread
成员方法。
Level
的值通常是
1
到
10
,也就是
MIN_PRIORITY
到
MAX_PRIORITY
的范围内。
线程同步
当两个或两个以上的线程要共享一个资源是,它们需要某种方法来确定同一时刻只能有一个线程占用资源。达到此目的的过程叫做同步
(synchronization)
。
使用同步方法,当一个线程正在同步方法内所有要访问该同步方法的线程必须等侍,一个例子:
package com.jdk.thread;
class Callme{
public void call(String msg){
System.out.print("["+msg);
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("]");
}
}
class Caller implements Runnable{
String msg;
Thread t;
Callme target;
public Caller(Callme targ,String s){
target=targ;
msg=s;
t=new Thread(this);
t.start();
}
public void run() {
target.call(msg);
}
}
public class SynchDemo {
public static void main(String[] args) {
Callme callme=new Callme();
Caller c1=new Caller(callme,"Hello"); //
通过多线程创建对象
Caller c2=new Caller(callme,"JAVA");
Caller c3=new Caller(callme,"C++");
try{
c1.t.join();
c2.t.join();
c3.t.join();
}catch(Exception e){
e.printStackTrace();
}
System.out.println("
主线程结束
!");
}
}
输出的结果是:
[Hello[JAVA[C++]
]
]
主线程结束
!
主线程结束
!
在这里必须要记住多线程的同步访问,只指对同一个对象,也就是说只有同时多个线程访问一个对象的数据或方法时才会出现以上这种情况。因为多线程的
Caller
类,同时访问了同一个对象
callme
的同一个方法
call()
所以就出现以上的输了结果,显然这不是我们想要的。
如果我们在
Callme
类的
call
()方法前加上
synchronized
方法如下:
public synchronized void call()
;
得到的结束就是
:
[Hello]
[JAVA]
[C++]
主线程结束
!
线程间的通讯
为了避免轮询,
JAVA
包含了通过
wait()
,
notify()
和
notifyAll()
实现一个进程间的通信机制。这些方法在对象中是用
final
的方法实现的。所以所有的类都含有它们,这三个方法仅在
synchronized
方法中可以调用。
Wait()
告知被调用的线程放弃管程,进入睡眠直到其它线程进入相同的管程并调用
notify()
。
Notify()
恢复相同对象中第一个调用
wait()
的线程。
notifyAll()
恢复相同对象中所有调用
wait()
的线程,具有最高优先级的最先运行。
这些方法在
Object
类中被声明如下:
public void wait() throws InterruptedException
public void waitAll();
public void notify();
下面是一个不使用经程间通信的例程,它输入的结果并不是我们想要的,代码如下:
package com.jdk.thread;
class Q{
int n;
synchronized int get(){
System.out.println("get "+n);
return n;
}
synchronized void put(int vn){
this.n=vn;
System.out.println("put "+n);
}
}
class Producer implements Runnable{
Q q;
public Producer(Q vq){
this.q=vq;
new Thread(this,"Producer").start();
}
public void run() {
int i=0;
while(true){
q.put(i++);
}
}
}
class Consumer implements Runnable{
Q q;
public Consumer(Q vq){
this.q=vq;
new Thread(this,"Consumer").start();
}
public void run() {
while(true){
q.get();
}
}
}
public class SynchDemo1 {
public static void main(String[] args) {
Q q=new Q();
new Producer(q);
new Consumer(q);
System.out.println("
主线程结束
!");
}
}
尽管在
Q
类中的
put
()和
get()
方法是同步的,没有东西阻止生产者超越消费者。也没有东西阻止消费者消费同样序列多次,所以输出结果如下:
get 19000
put 19001
get 19001
put 19002
get 19002
通常把
Q
类改成如下,但我还是有点不理解,为什么输出结果不是从
1
开始呢?进程中的线程是怎么通信的呢?
class Q{
int n;
boolean valueSet=false;
synchronized int get(){
if(!valueSet){
try{
wait();
}catch(Exception e){
e.printStackTrace();
}
}
System.out.println("get "+n);
valueSet=false;
notify();
return n;
}
synchronized void put(int vn){
if(valueSet){
try{
wait();
}catch(Exception e){
e.printStackTrace();
}
}
this.n=vn;
valueSet=true;
System.out.println("put "+n);
notify();
}
}
死锁
需要避免的与多任务和理有关的特殊的错误类型是死锁,死锁发生在当两个线程对一对同步对象有循环的依懒关系时。
线程挂起、恢复、终止
posted on 2006-10-07 20:48
有猫相伴的日子 阅读(433)
评论(0) 编辑 收藏 所属分类:
jdk