河北神马推广多少钱:多线程/JAVA多线程 - 魔乐培训 高端JAVA培训 魔乐科技JAVA培训
来源:百度文库 编辑:中财网 时间:2024/05/06 05:12:05
多线程
发布日期:2008-10-30 8:57:00 发布者:[IT电子教育门户] 评论:[ 8] 浏览: 8501、课程名称:多线程
多线程的基本作用、两种实现的区别、同步与死锁的概念。
在开发中真正去编写多线程代码是很少的,要的只是基本概念,本章就是基本概念。
2、知识点
2.1、上次课程的主要知识点
1、 包的定义及使用
2、 四种访问控制权限
2.2、本次预计讲解的知识点
1、 多线程的概念
2、 多线程的两种实现方式及区别
3、 多线程的主要操作方法
4、 同步与死锁的概念
3、具体内容
3.1、多线程的基本概念
JAVA是少数几个支持多线程的语言,所以多线程也就是成为了JAVA的一个特点。
最早的DOS系统一旦有病毒进入,则立刻死机,因为在同一个时间段上,永远只能有一个进程执行。
而windows就算是有病毒产生了,则系统照样可以执行。因为是属于多进程的操作系统。
进程:每一个应用程序的启动就称为进程。
线程:是在进程的基础之上划分出来的比进程更小的时间单位。
线程的运行速度要超过进程。
一个操作系统虽然可以跑多个进程,但是对于整个电脑而言只有一个CPU,所以在同一个时间段上CPU会处理多个程序,而在同一个时间点上CPU只会处理一个程序。
3.2、线程的实现
在JAVA中要想实现一个多线程的程序有两种方式:
• 继承Thread类
• 实现Runnable接口
3.2.1、继承Thread类实现
一个类只要继承了Thread类,那么此类就称为多线程的操作类。
例如:以下代码
class MyThread extends Thread{
};
线程在操作之中应该存在线程的主体,线程的主体方法为:run方法,所以Thread的子类必须覆写run方法。
例如:以下代码覆写了run方法
class MyThread extends Thread{
private String name ;
public MyThread(String name){
this.name = name ;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
public class ThreadDemo01{
public static void main(String args[]){
MyThread mt1 = new MyThread("线程A ") ;
MyThread mt2 = new MyThread("线程B ") ;
mt1.run() ;
mt2.run() ;
}
};
编译并运行程序,观察效果。从运行结果可以发现,是A执行完之后再执行B,但是之前的程序并没有同时运行,因为此时,线程并没有启动,如果要想启动线程则必须调用Thread类中的start()方法才可以。例如:修改之前的代码:
class MyThread extends Thread{
private String name ;
public MyThread(String name){
this.name = name ;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
public class ThreadDemo02{
public static void main(String args[]){
MyThread mt1 = new MyThread("线程A ") ;
MyThread mt2 = new MyThread("线程B ") ;
mt1.start() ;
mt2.start() ;
}
};
发现此时的运行结果是,两个线程之间交替执行。
问题?
既然start()方法调用的还是run方法,那为什么不直接调用run方法呢?
观察JDK中的Thread类实现
start()方法的定义:
public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
started = true;
start0();
}
private native void start0();
如果一个线程已经启动了之后再启动第二次,则会报非法的线程状态。
在JAVA中有一种技术称为:JNI技术,表示的是JAVA本地接口,可以通过JAVA调用本地操作系统中的函数支持。所以线程的实现靠的是操作系统的支持。
如果一个多线程的操作,使用了Thread类来完成的话,那么此类就不能继承其他的类了。
3.2.2、实现Runnable接口
实现Rrnnable接口同时覆写run方法
class MyThread implements Runnable{
private String name ;
public MyThread(String name){
this.name = name ;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
但是Runnable接口与Thread类不同的是,Runnable接口中并没有关于start()方法的定义,而只有Thread类中才有。继续观察Thread类。
观察Thread类中有如下两个构造方法:
• public Thread(Runnable target)
• public Thread(Runnable target,String name)
例如:使用以上的操作启动多线程
class MyThread implements Runnable{
private String name ;
public MyThread(String name){
this.name = name ;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
public class ThreadDemo03{
public static void main(String args[]){
MyThread mt1 = new MyThread("线程A ") ;
MyThread mt2 = new MyThread("线程B ") ;
new Thread(new MyThread("线程A")).start() ;
new Thread(new MyThread("线程B")).start() ;
}
};
3.2.3、Thread类与Runnable接口的关系
1、 Thread也是Runnable接口的子类
• public class Thread extends Object implements Runnable
2、 Thread类中有单继承的局限,而Runnable中并没有此局限
3、 使用Runnable实现的多线程操作类,适合于多个线程对象共享数据
例如:验证资源的共享
A、 使用Thread类完成
class MyThread extends Thread{
private int ticket = 10 ;
public void run(){
for(int i=0;i<100;i++){
if(ticket>0){
System.out.println("卖票。ticket = " + ticket--) ;
}
}
}
};
public class ThreadDemo04{
public static void main(String args[]){
MyThread mt1 = new MyThread() ;
MyThread mt2 = new MyThread() ;
MyThread mt3 = new MyThread() ;
mt1.start() ;
mt2.start() ;
mt3.start() ;
}
};
整个程序一共卖出了30张票,但是实际上只有10张票,这是因为每一个线程对象都拥有各自的10张票。而如果现在使用Runnable接口实现的话,则可以达到资源的共享
B、 使用Runnable接口实现
class MyThread implements Runnable{
private int ticket = 10 ;
public void run(){
for(int i=0;i<100;i++){
if(ticket>0){
System.out.println("卖票。ticket = " + ticket--) ;
}
}
}
};
public class ThreadDemo05{
public static void main(String args[]){
MyThread mt = new MyThread() ;
new Thread(mt).start() ;
new Thread(mt).start() ;
new Thread(mt).start() ;
}
};
3.3、线程的状态
所有的线程虽然在语句上有先有后,但是是同时启动的,谁先抢占到CPU资源,谁就运行。
线程创建完成之后,等待启动,启动之前先进入到就绪状态。之后等待CPU调度,调度之后进入运行状态。
如果中间有问题了,导致程序暂停执行,则进入到阻塞状态。回到就绪状态,等待再次执行。
当全部程序执行完之后,就进入到终止状态了。
所有的线程并不是一调用start()方法就立刻启动,而是要等待CPU调度才可以执行。
3.4、线程的主要操作方法
线程中的所有操作方法都是在Thread类中定义的。
3.4.1、取得当前正在运行的线程名称
在Thread类中有以下一个方法可以取得当前正在运行的线程对象。
• public static Thread currentThread()
Thread类的构造方法:
• public Thread(Runnable target,String name)
• public Thread(String name)
取得线程的名称:public final String getName()
设置线程的名称:public final void setName(String name)
可以通过Thread类中的setName()方法设置一个线程的名称,但是最好在线程启动之前进行设置。
例如:以下代码取得了当前正在运行的线程名称
class MyThread implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
}
}
};
public class ThreadDemo06{
public static void main(String args[]){
MyThread mt = new MyThread() ;
new Thread(mt).start() ;
new Thread(mt).start() ;
new Thread(mt).start() ;
}
};
发现此时线程的名称很有规律:Thread-0、Thread-1、Thread-2,…
里面肯定有一个static的属性,用于保存当前产生了多少个对象。
例如:直接为线程命名
class MyThread implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
}
}
};
public class ThreadDemo07{
public static void main(String args[]){
MyThread mt = new MyThread() ;
new Thread(mt,"线程A").start() ;
new Thread(mt,"线程B").start() ;
new Thread(mt,"线程C").start() ;
new Thread(mt).start() ;
new Thread(mt).start() ;
}
};
分析以下程序的执行结果:
class MyThread implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
}
}
};
public class ThreadDemo08{
public static void main(String args[]){
MyThread mt = new MyThread() ;
new Thread(mt,"线程").start() ;
mt.run() ; ? 主方法直接调用的run()方法
}
};
观察输出结果:
• 发现有一个main线程的存在。
一个JAVA程序至少启动几个线程?是两个
• MAIN:主线程
• 垃圾收集线程
3.4.2、线程的休眠
对于线程可以允许其稍微暂停运行,使用休眠的语法即可完成,方法定义如下:
• public static void sleep(long millis) throws InterruptedException
• 此方法会抛出中断异常
例如:以下程序验证了sleep()方法的使用
class MyThread implements Runnable{
public void run(){
for(int i=0;i<10;i++){
try{
Thread.sleep(500) ;
}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "运行, i = " + i) ;
}
}
};
public class ThreadDemo09{
public static void main(String args[]){
MyThread mt = new MyThread() ;
new Thread(mt,"线程").start() ;
mt.run() ;
}
};
3.4.3、线程的中断
之前休眠方法上有一个中断的异常抛出,实际上线程是可以中断的。
如果要想中断一个线程则必须使用另外一个线程控制,中断线程的方法:
• public void interrupt()
例如:以下代码演示了中断线程的操作
class MyThread implements Runnable{
public void run(){
System.out.println("********* 进入run方法 **********") ;
try{
System.out.println(" =========== 开始休眠 ===============") ;
Thread.sleep(20000) ;
System.out.println(" =========== 结束休眠 ===============") ;
}catch(Exception e){
System.out.println(e) ;
// 表示直接返回到方法的调用处
return ;
}
System.out.println("********* 完成run方法 **********") ;
}
};
public class ThreadDemo10{
public static void main(String args[]){
MyThread mt = new MyThread() ;
Thread t = new Thread(mt,"线程") ;
t.start() ;
try{
// 为了可以让程序多休眠一会儿
Thread.sleep(1000) ;
}catch(Exception e){
}
// 中断线程执行
t.interrupt() ;
}
};
3.4.4、线程的强制执行
可以使用一个方法让一个线程强制的执行下去。方法的定义为:
• public final void join() throws InterruptedException
例如:以下代码演示了join的作用
class MyThread implements Runnable{
public void run(){
int i = 0 ;
while(true){
System.out.println(Thread.currentThread().getName() + " --> i = " + i++) ;
}
}
};
public class ThreadDemo11{
public static void main(String args[]){
Thread t = new Thread(new MyThread(),"==线程==") ;
int i = 0 ;
t.start() ;
while(true){
if(i>=100){
// 强制运行
try{
t.join() ;
}catch(Exception e){}
}
System.out.println("MAIN " + i++) ;
}
}
};
3.5、线程的同步与死锁
4、总结
1、 线程可以不掌握代码,但是所有的概念必须非常清楚
2、 进程与线程的区别
• 线程是在进程的基础之上的进一步划分
• 如果没有进程了,则线程也不存在
3、 JAVA中线程实现的两种方式
• 继承Thread类,不能共享数据
• 实现Runnable接口,可以共享数据
• 使用Runnable的最大好处是可以避免单继承带来的局限
4、 JAVA中一个java命令实际上启动的是一个JVM进程,一个JVM进程至少启动两个线程:
• MAIN线程
• GC线程
5、 线程的主要操作方法
• currentThread()、getName()、sleep()