作用:
使用多线程技术可以使系统同时运行多个执行体,这样可以加快线程的响应时间,提高计算机资源使用率。
线程生命周期
1.新生态
用new 运算符和Thtead类或子类建立线程对象后,该线程对象就处于新生态。处于新生态的线程有自己的内存空间,通过调用start()方法后进入就绪态。
2.就绪态
处于就绪态的线程已具备运行条件,但还未分配到cpu,因为线程将进入线程队列,等待系统为其分配cpu。一旦获得cpu,线程就进入运行态并自动调用自己的run()方法。
3.运行态
在运行态的线程执行自己run()方法中的代码,直到调用其他方法而终止,或等待某资源而阻塞,或完成任务而死亡。
4.阻塞态
执行sleep()方法,或等待I/O设备资源让出cpu并暂时终止自己的运行,进入阻塞态。
5.死亡态
死亡态有两个原因:一个是正常运行的线程完成它所有的工作;一个是被强制终止,如执行stop()方法或destroy()方法。
继承Thread实现多线程
package com.neusoft.thread;
public class ThreadDome extends Thread {
String s;
int m,count=0;
ThreadDome(String str,int mm){
s=str;
m=mm;
}
public void run(){
try {
while(true){
System.out.print(s);
sleep(m);
count++;
if(count>=20)
break;
}
System.out.println("finished !"+s);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadDome threadA =new ThreadDome("A ",50);
ThreadDome threadB =new ThreadDome("B ",100);
threadA.start();
threadB.start();
}
}
运行结果:
调用Runnable接口实现多线程
package com.neusoft.thread;
public class Thread_Runnable implements Runnable{
String s;
int m,count=0;
Thread_Runnable(String str ,int mm){
s=str;
m=mm;
}
public void run(){
try {
while (true){
System.out.print(s);
Thread.sleep(m);
if(++count>=20)
break;
}
System.out.println("finishes!"+s);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread_Runnable threadA=new Thread_Runnable("A ",50);
Thread_Runnable threadB=new Thread_Runnable("B ",100);
Thread threadAA=new Thread(threadA);
Thread threadBB=new Thread(threadB);
threadAA.start();
threadBB.start();
}
}
运行结果:
小结:
优先使用runnable接口实现多线程:
不论是那种方式,最后都需要通过Thread类的实例调用start()方法来开始线程的执行,start()方法通过java虚拟机调用线程中定义的run方法来执行该线程。通过查看java源程序中的start()方法的定义可以看到,它是通过调用操作系统的start0方法来实现多线程的操作的。
但是一般在系统的开发中遇到多线程的情况的时候,以实现Runnable接口的方式为主要方式。这是因为实现接口的方式有很多的优点:
1、就是通过继承Thread类的方式时,线程类就无法继承其他的类来实现其他一些功能,实现接口的方式就没有这中限制;
2.也是最重要的一点就是,通过实现Runnable接口的方式可以达到资源共享的效果。
但是其实两种方式是有密切联系的:
我们通过实现接口Runnable建立的MyThread类,而Thread类也是实现了Runnable接口的子类。如果我们想启动线程,需要通过Thread类中的start()方法,Thread类中的start()方法来调用MyThread类中run方法来执行该线程。这个实现是典型的代理模式。
Join()方法:
Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
线程同步:
实现线程同步操作的方法是在共享内存变量的方法前加synchronized修饰符。在程序运行过程中,如果某线程调用synchronized修饰的方法,在该线程结束此方法的运行前,其他线程不能运行该方法。
package com.neusoft.thread;
public class Thread_Synchronized implements Runnable {
//模拟银行存取款
private static float s=2000; //原有存款
private static float temp;
public static synchronized void deposit(float amt){ //存款
temp=s;
temp+=amt;
try {
int mm=(int) (1000*Math.random());
Thread.sleep(mm);
System.out.print(mm+"毫秒,");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s=temp;
System.out.println(s);
}
public void run() {
// TODO Auto-generated method stub
for(int i=1;i<=4;i++){
Thread_Synchronized.deposit(100);
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread_Synchronized threadA=new Thread_Synchronized();
Thread_Synchronized threadB=new Thread_Synchronized();
Thread threadAA=new Thread(threadA);
Thread threadBB=new Thread(threadB);
threadAA.start();
threadBB.start();
}
}
面试问答:
1.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
答:
有两种实现方法,分别是继承Thread类与实现Runnable接口
用synchronized关键字修饰同步方法
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
2.sleep() 和 wait() 有什么区别?
答:sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。
3.同步和异步有何异同,在什么情况下分别使用他们?
答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率
4.同步有几种实现方法
答: 同步的实现方面有两种,分别是synchronized ,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
5.启动一个线程是用run()还是start()?
答: 启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。