Java 多线程编程
来源:互联网 发布:希腊语词根知乎 编辑:程序博客网 时间:2024/06/06 05:21
理解进程与线程的区别
为了增加操作系统运行的效率,设计出了进程,但是进程的启动和销毁还是很慢的,所以后来人们开始尝试在进程上做进一步优化,产生了线程的概念,即 :线程实在进程的基础上扩充的。线程的启动和销毁将比进程更快,一个进程上可以划分出很多个线程,而进程消失,线程一定消失。Java是为数不多的支持多线程编程的语言之一。
简单来说:
多进程:同一时间段会有多个程序并发执行,轮流抢占CPU资源。
多线程:同一进程内可并发多线程,共享内存单元,所有可以实现同一程序内不同代码块并发执行、访问同一数据。
具体的进程与线程的关系参考 《计算机操作系统》。
Java多线程实现的两种方式
如果要想实现多线的开发,也需要一个线程的主体类,这个类必须继承Thread类或者实现Runnable接口。(实际的开发来讲一定使用的是Runnable接口)
1、继承Thread类
线程的主体类只需要通过extends继承Thread类,那么这个类就可用于线程的控制,当然继承之后用户还需要覆写Thread类之中的run()方法。
public class Thread extends Object implements Runnable{}
run()方法与start()方法
//调用run()方法:class MyThread extends Thread { private String name ; public MyThread(String name) { this.name = name; } @Override public void run() { for (int x = 0 ; x < 10 ; x++ ) { System.out.println( this.name + ": x = " + x ) ; } }}public class Demo { public static void main(String args[]) throws Exception{ MyThread mtA = new MyThread("Thread A") ; MyThread mtB = new MyThread("Thread B") ; MyThread mtC = new MyThread("Thread C") ; mtA.run() ; mtB.run() ; mtC.run() ; }}
运行结果:
可以看到,三个线程是先后独立完成,并没有多线程并发执行。这里如果想要实现多线程并发执行,应该调用start()方法:
Thread类的start()方法:
public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
抛出:
IllegalThreadStateException - 如果线程已经启动。
调用start()方法的程序结果(因为线程调度和操作系统有关,所以这里的运行结果也取决于操作一同的调度方案,并且每次执行的结果都不相同):
为什么要通过start() 调用 run() 呢?
下面来看一下Java源码中的start() 方法:
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }private native void start0() {};
调用start() 方法的时候会调用start0() 方法,而在star0() 方法的声明处有一个native关键字,次关键字表示通过本地的原始函数进行代码的实现。
线程的操作一定是需要通过CPU资源调度,每个操作系统的资源调度是不一样的,所以此方法实际上会有JVM来实现,那么编写的时候只给出一个抽象方法的名称,而具体的实现将根据不同的操作系统进行覆写,所以才实现了可移植性。
所以可以得出结论,当用户执行start() 方法的时候,将进行操作系统的资源调度,调度之后才会执行,所以任何时候只要是启动多线程,都一定要使用Thread类之中的start() 方法。
本方法会抛出一个“IllegalThreadStateException”异常,用throw抛出正常情况下应该用try catch来捕获处理,但是这里面没有捕获处理,因为通过观察“IllegalThreadStateException”的继承结构
> java.lang.Object > java.lang.Throwable> java.lang.Exception> java.lang.RuntimeException> java.lang.IllegalArgumentException> java.lang.IllegalThreadStateException
发现继承关系中有RuntimeException类,所以可以不做处理。此异常的触发:当一个线程被重复启动的时候抛出次异常。所以一个线程只能够启动一次。
2、实现Runnable接口(解决单继承局限)
public interface Runnable { public void run() ; //所有方法只有run(),没有start() }
通过之前的分析可以得出结论,只要是线程的启动一定要依靠Thread类的start() 方法,而Runnable接口之中是没有start() 方法的。那么接下来继续看一下Thread类中构造方法的定义:
public Thread(Runnable target) ; 发现可以接受一个Runnable对象。所以可以通过Thread类的构造函数调用start()方法。
//通过实现Runnable接口实现多线程class MyThread implements Runnable { private String name ; public MyThread(String name) { this.name = name; } @Override public void run() { for (int x = 0 ; x < 10 ; x++ ) { System.out.println( this.name + ": x = " + x ) ; } }}public class Demo { public static void main(String args[]) throws Exception{ MyThread mtA = new MyThread("Thread A") ; MyThread mtB = new MyThread("Thread B") ; MyThread mtC = new MyThread("Thread C") ; new Thread(mtA).start() ; new Thread(mtB).start() ; new Thread(mtC).start() ; }}
两种实现方法的区别
通过上面两种多线程方法的实现,我们已经知道了二者之间实现结构是一样的,继承限制不一样,但是还有其他的区别吗?
观察Thread类的定义
public class Thread extends Object implements Runnable
从结构上讲Thread是一个代理类的结构设计,但又不是那么完整,如果是一个纯粹的代理结构应该调用的是Runnable接口中的run() 方法,但此处调用的是Thread类的start() 方法。所以在整个的操作之中,虽然形式是代理结构,但还是有些差异的,而这个差异也是由于历史原因造成的。
数据共享
除了上述的基本联系之外,二者还有一点小区别:使用Runnable接口实现的多线程要比使用Thread类实现的多线程更容易表示出数据共享的概念。
范例程序:编写一个简单的卖票程序,利用Thread类实现(产生三个线程)
// 方法一:继承Thread类class MyThread extends Thread { private int ticket = 5 ; @Override public void run() { for (int x = 0 ; x < 10 ; x++ ) { if (this.ticket > 0) { System.out.println( "卖票: ticket = " + this.ticket -- ) ; } } }}public class Demo { public static void main(String args[]) throws Exception{ //启动三个线程,但是这里生成了三个MyThread类对象,每个都有自己ticket属性,所以。。。。 new MyThread().start() ; new MyThread().start() ; new MyThread().start() ; }}
运行结果:
//方法二:实现Runnable接口class MyThread implements Runnable { private int ticket = 5 ; @Override public void run() { for (int x = 0 ; x < 10 ; x++ ) { if (this.ticket > 0) { System.out.println( "卖票: ticket = " + this.ticket -- ) ; } } }}public class Demo { public static void main(String args[]) throws Exception{ //启动三个线程,这里只成了一个MyThread类对象,ticket属性是共享的 Runnable mt = new MyThread() ; new Thread(mt).start() ; new Thread(mt).start() ; new Thread(mt).start() ; }}
运行结果:
通过观察上述两个例子可以看出来,方法二更容易实现数据共享。原因在于:方法一继承Thread类,当要实现多线程并发执行时,需要生成多个类对象,每个类对象各自调用start(),此时对象之间相互独立,每个对象都有自己ticket属性,造成上述结果;方法二只定义了一个类对象Runnable mt = new MyThread() ;,这样启动的三个线程时,ticket属性都是该类对象mt的属性,实现了数据的共享。
二者区别总结
- 多线程的两种实现方式,继承Thread类或实现Runnable接口,其中Thread类是Runnable的子类;
- 如果继承了Thread类,会受到单继承局限,而且不方便表示数据共享的概念;
- 如果实现Runnable类,没有单继承局限,而且方便表示数据共享。
- 不管使用何种方式,最终一定要通过Thread类的start() 方法才可以启动多线程。
- 日后多线程的主体结构定义在实现Runnable接口的类中,而多线程的实现使用Thread类的start() 方法。
定义匿名内部类来实现多线程编程
//定义匿名内部类来实现多线程编程public class Demo { public static void main(String args[]) throws Exception{ //这里的结构为new Thread( new Runnable() {} ).start() ; 大括号内定义匿名内部类 new Thread(new Runnable() { private int ticket = 5 ; @Override public void run() { for (int x = 0 ; x < 10 ; x++ ) { if (this.ticket > 0) { System.out.println( "卖票: ticket = " + this.ticket -- ) ; } } } }).start() ; }}
- 【JAVA】JAVA多线程编程
- 【java】:java多线程编程
- Java多线程编程初步
- Java 多线程编程
- Java多线程编程详解
- Java多线程编程经验谈
- Java 多线程编程
- Java多线程编程详解
- Java多线程编程详解
- Java 5.0多线程编程
- Java 5.0多线程编程
- Java 5.0多线程编程
- Java多线程编程详解
- Java多线程编程详解
- Java 5.0多线程编程
- Java多线程编程详解
- java基础教程-多线程编程
- Java多线程编程详解
- 线程进程初识
- Java时间的格式化、大小比较
- C++命名空间
- Java项目开启远程调试(tomcat、springboot)
- Unity3d实现扭动挤压浏览效果
- Java 多线程编程
- tp5改写跳转提示页面
- HDOJ 2094&&map水过
- 异常com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException
- CS20SI Tensorflow for Deeplearning课程笔记(一)
- 感知机算法
- 单片机实验4
- HttpUtil
- document.ready和onload的区别