也许我们经常做的事就是一边聊QQ一边听音乐,有的还一边玩游戏,这一切都是多任务实现的。而java语言使用多线程实现了一个程序中的多个任务同时运行。
那到底是不是同时执行多线程呢?显然不是,CPU一次只能然一个线程执行,由于线程之间切换速度很快,所以在我们看来是同时执行的。
java中如何实现多线程:
1.继承Thread类创建多线程。
2.实现Runnable接口创建多线程。
继承Thread类,重写run()方法是实现多线程的方式之一,Thread类本身就实现了Runnable接口。
如下例继承Thread类:
public class ThreadTest extends Thread{
public void run(){
while(true){
String name = Thread.currentThread().getName();
sayHello(name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void sayHello(String name){
System.out.println(name+"说hello");
}
public static void main(String [] agrs){
ThreadTest tt1 = new ThreadTest();
ThreadTest tt2 = new ThreadTest();
tt1.start();
tt2.start();
}
}
我的运行结果(每个人的运行结果都会不同):
Thread-0说hello
Thread-1说hello
Thread-1说hello
Thread-0说hello
Thread-1说hello
Thread-0说hello
Thread-1说hello
Thread-0说hello
可以看出线程是一个一个执行的,并且不是按顺序执行的,红色标记的地方说明。cpu为每个线程分配了时间片,哪个线程得到了CPU,哪个线程就得到执行,这个在后面再说明。
实现Runnable接口,实现run()方法来创建多线程,如下例:
public class ThreadTest implements Runnable{
public void run() {
while(true){
String name = Thread.currentThread().getName();
sayHello(name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void sayHello(String name){
System.out.println(name+"说hello");
}
public static void main(String [] agrs){
Runnable r = new ThreadTest();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
}
}
这里只是创建线程对象有稍微的区别。
那么他们之间有什么区别?甚至有的人问哪个更好?因为我就这么问过,但是在网上找到的答案基本都很片面,只找到了一个说的比较好,还需要时间研究研究。我列出来大家看看:
1.适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想。
2.可以避免由于java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。
3.有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以操作相同的数据,与它们的代码无关。当共享访问相同的对象时,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例
那么实现多线程都是对run()方法重写,为什么不是直接调用run()方法而是调用start()方法呢?这里我们来看看run()和start()方法的区别。
start():
通过调用start()方法来启动线程,此时线程处于就绪状态,一旦得到CPU时间片就执行run()方法,方法run()称为线程体,它包含了要执行这个线程的内容,当run()方法运行结束,线程随即终止。
run():
run()方法只是Thread类的一个普通方法,如果直接调用run()方法,程序中只有主线程这一个线程,执行路径只有一条,要这个方法执行完才能执行下面的代码,这就没达到多线程的目的。
继续看如上例子,如果是:
thread1.run();
thread2.run();
那么运行结果为:
main说hello
main说hello
main说hello
main说hello
main说hello
main说hello
main说hello
main就是我们一直以来接触的程序的入口(主方法),其实它本身就是一个线程,并且是主线程。
线程的生命周期:
线程具有生命周期,包含3个状态,使用线程实例调用start()方法之前,线程处于出生状态,当调用start()方法后,线程就处于就绪状态,当线程得到系统资源后进入运行状态,当时间片结束线程又回到就绪状态,继续等待资源。
而在线程执行过程中,线程又可能转入等待,睡眠,阻塞或者死亡状态。
1.线程在执行过程中调用wait()方法进入等待状态,这时线程释放持有的资源,必须采用notify()或者notifyAll()方法来唤醒该进程或者被别的对象调用interrupt()方法打断,这个方法会抛出一个InterruptedException异常,这个异常如果不捕获处理,线程就会异常终止,当线程被唤醒后,再次进入就绪状态,等待资源。
2.线程在执行过程中调用sleep()方法,进入睡眠状态,这个过程线程不会释放持有的资源,待睡眠时间完后即进入就绪状态,不会立即进入执行状态,因为线程调度机制恢复线程的运行也需要时间。当然睡眠状态也会被interrupt(0方法打断,效果和wait()一样。
3.线程执行过程中,因为某些原因而使其停止执行。当线程阻塞后,别的贤臣就有机会执行,当线程阻塞被解除后进入就绪状态,等待被调度。
阻塞原因有:
a.线程调用sleep()方法放弃CPU时间片。
b.线程调用wait()方法,等待别的线程来唤醒它。
c.线程调用阻塞IO方法,方法返回前处于阻塞状态。(IO后面会分析)
d.线程执行需要资源,在执行过程中,某些资源被别的线程持有,则该线程会处于阻塞状态来等待资源的获取。
e.程序调用suspend()方法将该线程挂起,该方法容易产生死锁。
4.线程进入死亡状态也是有原因的。
死亡原因:
a.线程执行完run()方法进入死亡状态。
b.线程通过某种方式终止线程使其进入死亡状态(接下来分析)。
c.线程抛出未捕获的线程使线程异常终止。
终止线程:
在早期的JDK中提供了stop()方法来终止线程,但是新版本不建议使用,提倡用布尔标记控制循环的停止来达到终止线程的目的。
1.在循环体中,通过条件判断,符合条件的就break;跳出循环,终止线程。
2.循环条件不要写成true,而是手动控制循环条件的false来达到终止线程的目的。
线程的调度:
1.线程执行前,通过优先级来达到线程调度的目的,优先级高的线程优先得到CPU执行。线程的优先级可以在程序中表明该线程的重要级,学过操作系统的都明白有很多的调度算法,这里不细说了。
2.线程执行过程中,可以通过join()方法来调度线程,比如:
threadA = new Thread(new Runnable(){
public void run() {
for(int i = 0;i<=100;i++){
if(i >= 50){
threadB.join();
}
}
}
});
显然在线程A执行时,当i=50的时候,就会执行线程B,直到线程B执行完,线程A才会继续执行,这样也能达到线程调度的目的。
线程安全:
在多线程中,说到什么最重要,要属线程安全当之无愧,引入多线程的目的就是对大量任务进行有序的管理,通过多个任务混合使用,可以更有效的利用计算机的资源。
使用多线程,会发生两个以上的线程抢占资源的问题,必须防止资源访问的冲突。java提供线程同步机制可以防止资源访问的冲突(下篇分析)。
欲知后事如何,请看下集。。。
分享到:
相关推荐
#### 三、Java多线程程序设计初步 ##### 1. 线程的创建 - **继承Thread类**: - 重写`run()`方法。 - **实现Runnable接口**: - 实现`run()`方法。 - 将`Runnable`对象传递给`Thread`构造函数。 ##### 2. 线程的...
│ 高并发编程第一阶段05讲、采用多线程方式模拟银行排队叫号.mp4 │ 高并发编程第一阶段06讲、用Runnable接口将线程的逻辑执行单元从控制中抽取出来.mp4 │ 高并发编程第一阶段07讲、策略模式在Thread和Runnable...
4. **线程同步机制**:如`synchronized`关键字或`Lock`对象,保证多线程环境下的数据一致性,防止并发问题。 5. **异常处理**:确保即使在出现异常的情况下,时钟仍然能够正常运行,避免程序崩溃。 从代码实现的...
在Java编程语言中实现贪吃蛇游戏,我们会涉及多线程技术,这是理解并发处理和提升程序性能的关键概念。下面我们将详细探讨如何使用Java实现一个多线程的Snake游戏。 首先,贪吃蛇游戏的核心是蛇的移动和食物的生成...
总的来说,Java游戏编程初步会引导初学者理解Applets的开发流程、线程的概念及其在游戏中的应用,为进一步深入Java游戏开发打下基础。虽然这个领域可能需要更多的学习,如图形库、物理引擎和网络编程,但这个起点为...
在学习过程中,应着重理解每个概念的实际应用场景,例如如何通过类和对象来抽象现实世界的问题,如何使用异常处理来增强程序的健壮性,以及如何利用多线程提升程序效率。同时,不断通过做题和编写小程序来锻炼解决...
本文将深入探讨多线程的基础知识,包括它的概念、使用场景、核心方法以及通过简单示例来帮助初学者理解多线程的实践操作。 1. 多线程概念: 多线程是指在一个应用程序中同时执行多个独立的执行路径,每个路径称为一...
通过龟兔赛跑的例子,学生已经初步了解了多线程的概念及编程方法。此时可以进一步提出思考题,例如如果乌龟和兔子在赛跑过程中需要吃东西 怎么办?如果乌龟和兔子都要吃同一个篮子里面 的食物怎么办?这时可以引入...
11. **多线程**:Java内置对多线程的支持,通过Thread类或实现Runnable接口可以创建并管理线程。 学习Java语言结构的过程中,不仅需要理解和掌握这些知识点,还要通过编写实际的代码来加深印象,不断实践和调试。配...
- Java内置对多线程的支持,可以通过实现Runnable接口或继承Thread类创建线程。 - 线程同步机制包括synchronized关键字、wait(), notify(), notifyAll()方法以及Lock接口。 8. **网络编程**: - Java提供了...
Java内置了对多线程的支持,允许开发者轻松创建和控制线程,这对于处理复杂的动画、游戏逻辑和用户输入非常有用。 - **Thread类**:是Java中表示线程的实体,可以通过继承`Thread`类并重写`run()`方法来自定义线程...
通过以上内容的学习,可以初步掌握Java多线程的基本概念和操作。然而,实际应用中还需要考虑线程优先级、线程池、线程通信、线程中断、异常处理等多个方面,这些都是Java线程编程的重要组成部分。在实践中不断探索和...
10. **多线程编程**: - 学习如何创建和管理线程,理解同步和互斥,使用 synchronized 关键字和 volatile 属性。 11. **网络编程**: - 理解套接字(Socket)编程,学会创建客户端和服务端的通信。 12. **Java ...
3. **类与对象**:Java是面向对象的编程语言,理解类的概念、对象的创建与销毁、封装、继承和多态性等核心概念。 4. **方法与函数**:学习如何定义和调用方法,了解参数传递机制,以及静态与非静态方法的区别。 二...
9. **多线程**:理解线程的概念,创建和控制线程,以及同步机制(如`synchronized`关键字和`wait()`,`notify()`方法)。 10. ** Swing GUI编程**:如果课程深入,可能会接触到图形用户界面的设计,如使用`JFrame`...
有了这些基础知识,学员将能够逐步构建更复杂的程序,并为进一步深入学习Java的高级特性,如面向对象编程、类库的使用、多线程以及数据库连接等做好准备。因此,对于希望投身IT行业的初学者而言,这是一个不可或缺的...
书中首先对Java语言的基础知识进行了详细介绍,这包括Java的核心概念、基础语法和基本结构,旨在让读者能够对Java语言有一个初步的认识和理解。之后,书中通过具体的实例,对Java语言进行了系统化的讲解,帮助读者更...