1、采用继承Thread类创建线程
定义一个java.lang.Thread类的子类,并重写其run方法:
class MyThread extends Thread{
public void run() { ……}
}
然后生成该类的对象:MyThread myThread = new MyThread(…);
2、通过实现Runnable接口创建线程
定义实现Runnable接口的线程类
class MyThread implements Runnable{
public void run() {……}
}
然后再利用Thread类构造线程类:
Thread myThread = new Thread (target);
Runnable中只有一个方法:public void run(); 用以定义线程运行体;
线程的启动:myThread.start();
两种方式的比较:
使用Runnable接口
可以将CPU,代码和数据分开,形成清晰的模型;
还可以从其他类继承;
保持程序风格的一致性;
在实现Runnable接口的类中可以使用静态方法如:Thread.currentThread()获取当前线程进行控制;
直接继承Thread类
不能再从其他类继承;
编写简单,可以直接操纵线程;
针对上述四种情况,使线程返回就绪状态的方法:
sleep()方法中的参数为休息时间,单位为毫秒,时间过去后,线程即为就绪的。
一个线程调用suspend()方法后,只能有其它线程调用它的resume()方法恢复。
如果一个线程等待条件变量,如果要停止等待,需要条件变量所在的对象调用notify()或者notifyAll()方法。
特定的I/O指令结束阻塞状态。
终止状态:有两种情况可导致线程终止
自然撤销,正常运行的线程完成了其全部工作;
调用stop()方法强制终止;
Thread提供了方法isAlive(),如果线程已经启动,但是未终止,返回true,反之,返回 false,表示该线程未启动,或者已终止。
如果isAlive()方法返回true,不能区分是就绪状态、阻塞状态还是运行状态。
注意事项:
对于任何状态,如果调用的方法和状态不符,都会引起非法状态处理异常。比如。线程刚创建后,只能调用 start ()或者 stop ()方法,如果调用其它方法就会引起非法状态处理。
sleep方法
可以调用Thread的静态方法:
public static void sleep(long mills) throws InterruptedException
示例:TestInterrupt.java
join方法
合并某个线程,是一种相对原始的线程间通信形式;
例如:在threadX中执行如下代码:
try{ threadY.join(); } catch ( InterruptedException e){…}
threadX将阻塞,一直等待threadY的消亡;
示例:TestJoin.java
yield方法:主动让出CPU,给同等级或更高级线程执行的机会;
不能滥用yield(),执行线程间的上下文切换可能导致过量的系统开销;1秒钟内不能超过5次;
示例:TestYield.java
守护线程(Daemon线程)
具有最低的优先级,用于在后台为系统中的其他对象和线程提供服务;一般应该是一个独立的线程,它的run()方法是一个无限循环.
将一个用户线程设置为守护线程的方式是在线程对象启动之前调用线程对象的setDaemon(boolean on)方法。
可以利用isDaemon()方法来判断一个线程是否为守护线程;
守护线程守护线程与其它线程的区别是,如果守护线程是唯一运行着的线程,程序会自动退出
典型的守护线程例子是JVM中的系统资源自动回收线程,它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
实现同步的方法例子没做
通过线程监视器,使用一个共享的条件变量,对线程同步进行更精准的控制
利用wait()、notify()、notifyAll()方法,来保证生产者与消费者的同步:生产一个,消费一个;
多线程同步机制总结:
对于多个线程共享的对象或方法,使用synchronized修饰;
若一个线程必须等待某个对象的状态改变的话,通过进入同步方法或调用wait()方法实现;
每当一个方法修改共享对象的状态时,应调用nofity()或notifyAll()进行通知;
死锁问题
线程能够被阻塞,当支持其运行所需的资源被另外一个阻塞线程锁定时,将会形成所谓的死锁(Deadlock);
Java语言自身没有提供能预防死锁的机制
死锁可能潜伏在运行的程序中,防止死锁是设计阶段的一个重要任务;
哲学家吃饭问题;死锁示例:TestDeadLock.java
解决死锁问题的方法:给条件变量施加排序;
1. 线程间的通信可以用管道流.
创建管道流:
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
或:
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
1. 用过滤流包装管道流进行读写
PrintStream p = new PrintStream( pos );
p.println(“hello”);
DataInputStream d = new DataInputStream(pis);
d.readLine();
2. 通过一个中间类来传递信息.
管道流可以连接两个线程间的通信
下面的例子里有两个线程在运行,一个往外输出信息,一个读入信息.
将一个写线程的输出通过管道流定义为读线程的输入.
outStream = new PipedOutputStream();
inStream = new PipedInputStream(outStream);
new Writer( outStream ).start();
new Reader( inStream ).start();