3、具体内容

从多线程开始,Java正式进入到应用部分,而对于多线程的开发,从Java EE上表现的并不是特别多,但是在Android开发之中使用较多,并且需要提醒的是,笔试或面试的过程之中,多线程所问到的问题是最多的。

3.1、多线程的基本概念

如果要想解释多线程,那么首先应该从单进程开始讲起,最早的DOS系统有一个最大的特征:一旦电脑出现了病毒,电脑会立刻死机,因为传统DOS系统属于单进程的处理方式,即:在同一个时间段上只能有一个程序执行。后来到了windows时代,电脑即使(非致命)存在了病毒,那么也可以正常使用,只是慢一些而已,因为windows属于多进程的处理操作,但是这个 时候的资源依然只有一块,所以在同一个时间段上会有多个程序共同执行,而在一个时间点上只能有一个程序在执行,多线程是在一个进程基础之上的进一步划分, 因为进程的启动所消耗的时间是非常长的,所以在进程之上的进一步的划分就变得非常重要,而且性能也会有所提高。

所有的线程一定要依附于进程才能够存在,那么进程一旦消失了,线程也一定会消失,但是反过来不一定。而Java是为数不多的支持多线程的开发语言之一。

3.2、多线程的实现(重点)

在Java之中,如果要想实现多线程的程序,那么就必须依靠一个线程的主体类(就好比主类的概念一样,表示的是一个线程的主类),但是这个线程的主体类在定义的时候也需要有一些特殊的要求,这个类可以继承Thread类或实现Runnable接口来完成定义。

3.2.1 、继承Thread类实现多线程

java.lang.Thread是一个负责线程操作的类,任何的类只需要继承了Thread类就可以成为一个线程的主类,但是既然是主类必须有它的使用方法,而线程启动的主方法是需要覆写Thread类中的run()方法才可以。

范例:定义一个线程的主体类

class MyThread extends Thread { // 线程的主体类

private String title;

public MyThread(String title) {

this.title = title;

}

@Override

public void run() { // 线程的主方法

for (int x = 0; x < 50; x++) {

System.out.println(this.title + "运行,x = " + x);

}

}

}

现在按照道理来讲,已经出现了线程类,并且里面也存在了相应的操作方法,那么就应该产生对象并调用里面的方法,自然下面编写出了下的程序。

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt1 = new MyThread("线程A") ;

MyThread mt2 = new MyThread("线程B") ;

MyThread mt3 = new MyThread("线程C") ;

mt1.run() ;

mt2.run() ;

mt3.run() ;

}

}

但是以上的操作实话而言并没有真正的启动多线程,因为多个线程彼此之间的执行一定是交替的方式运行,而此时是顺序执行,即:每一个对象的代码执行完之后才向下继续执行。如果要想在程序之中真正的启动多线程,必须依靠Thread类的一个方法:public void start(),表示真正启动多线程,调用此方法后会间接调用run()方法。

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt1 = new MyThread("线程A") ;

MyThread mt2 = new MyThread("线程B") ;

MyThread mt3 = new MyThread("线程C") ;

mt1.start() ;

mt2.start() ;

mt3.start() ;

}

}

此时可以发现,多个线程之间彼此交替执行,但是每次的执行结果肯定是不一样的。通过以上的代码就可以得出结论:要想启动线程必须依靠Thread类的start()方法执行,线程启动之后会默认调用了run()方法。

疑问?为什么线程启动的时候必须调用start()而不是直接调用run()?

发现调用了start()之后,实际上它执行的还是覆写后的run()方法,那为什么不直接调用run()方法呢?那么为了解释此问题,下面打开Thread类的源代码,观察一下start()方法的定义。

public synchronized void start() {

if (threadStatus != 0)

throw new IllegalThreadStateException();

group.add(this);

boolean started = false;

try {

start0();

started = true;

} finally {

try {

if (!started) {

group.threadStartFailed(this);

}

} catch (Throwable ignore) {

}

}

}

private native void start0();

打开此方法的实现代码首先可以发现方法会抛出一个“IllegalThreadStateException”异常。按照之前所学习的方式来讲,如果一个方法之中使用了throw抛出一个异常对象,那么这个异常应该使用try…catch捕获,或者是方法的声明上使用throws抛出,但是这块都没有,因为这个异常类是属于运行时异常(RuntimeException)的子类。

java.lang.Object

|- java.lang.Throwable

|- java.lang.Exception

|- java.lang.RuntimeException

|- java.lang.IllegalArgumentException

|- java.lang.IllegalThreadStateException

当一个线程对象被重复启动之后会抛出此异常,即:一个线程对象只能启动唯一的一次。在start()方法之中有一个最为关键的部分就是start0()方法,而且这个方法上使用了一个native关键字的定义。

native关键字指的是Java本地接口调用(Java Native Interface),即:是使用Java调用本机操作系统的函数功能完成一些特殊的操作,而这样的代码开发在Java之中几乎很少出现,因为Java的最大特点是可移植性,如果一个程序只能在固定的操作系统上使用,那么可移植性就将彻底的丧失,所以,此操作一般只作为兴趣使用。

多线程的实现一定需要操作系统的支持,那么以上的start0()方法实际上就和抽象方法很类似没有方法体,而这个方法体交给JVM去实现,即:在windows下的JVM可能使用A方法实现了start0(),而在linux下的JVM可能使用了B方法实现了start0(),但是在调用的时候并不会去关心具体是何方式实现了start0()方法,只会关心最终的操作结果,交给JVM去匹配了不同的操作系统。

所以在多线程操作之中,使用start()方法启动多线程的操作是需要进行操作系统函数调用的。

3.2.2 、实现Runnable接口实现多线程

使用Thread类的确是可以方便的进行多线程的实现,但是这种方式最大的缺点就是单继承的问题,为此,在java之中也可以利用Runnable接口来实现多线程,而这个接口的定义如下:

public interface Runnable {

public void run();

}

分享:如何区分新老接口?

在JDK之中,由于其发展的时间较长,那么会出现一些新的接口和老的接口,这两者有一个最大的明显特征:所有最早提供的接口方法里面都不加上public,所有的新接口里面都有public。

范例:通过Runnable接口实现多线程

class MyThread implements Runnable { // 线程的主体类

private String title;

public MyThread(String title) {

this.title = title;

}

@Override

public void run() { // 线程的主方法

for (int x = 0; x < 50; x++) {

System.out.println(this.title + "运行,x = " + x);

}

}

}

这个时候和之前的继承Thread类区别不大,但是唯一的好处就是避免了单继承局限,不过现在问题也就来了。刚刚解释过,如果要想启动多线程依靠Thread类的start()方法完成,之前继承Thread类的时候可以将此方法直接继承过来使用,但现在实现的是Runable接口,没有这个方法可以继承了,为了解决这个问题,还是需要依靠Thread类完成,在Thread类中定义了一个构造方法:public Thread(Runnable target),接收Runnable接口对象。

范例:利用Thread类启动多线程

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt1 = new MyThread("线程A");

MyThread mt2 = new MyThread("线程B");

MyThread mt3 = new MyThread("线程C");

new Thread(mt1).start();

new Thread(mt2).start();

new Thread(mt3).start();

}

}

这个时候就实现了多线程的启动,而且没有了单继承局限。

3.2.3 、Thread类和Runnable接口实现多线程的区别(面试题)

现在Thread类和Runnable接口都可以做为同一功能的方式来实现多线程,那么这两者如果从Java的实际开发而言,肯定使用Runnable接口,因为可以有效的避免单继承的局限,那么除了这些之外,这两种方式是否还有其他联系呢?

为了解释这两种方式的联系,下面可以打开Thread类的定义:

public class Thread extends Object implements Runnable

发现Thread类也是Runnable接口的子类,而如果真的是这样,那么之前程序的结构就变为了以下形式。

这个时候所表现出来的代码模式非常类似于代理设计模式,但是它并不是严格意义上代理设计模式,因为从严格来讲代理设计模式之中,代理主题所能够使用的方法依然是接口中定义的run()方法,而此处代理主题调用的是start()方法,所以只能够说形式上类似于代理设计模式,但本质上还是有差别的。

但是除了以上的联系之外,对于Runnable和Thread类还有一个不太好区分的区别:使用Runnable接口可以更加方便的表示出数据共享的概念。

范例:通过继承Thread类实现卖票程序

package cn.mldn.demo;

class MyThread extends Thread { // 线程的主体类

private int ticket = 5; // 一共5张票

@Override

public void run() { // 线程的主方法

for (int x = 0; x < 50; x++) {

if (this.ticket > 0) {

System.out.println("卖票,ticket = " + this.ticket --);

}

}

}

}

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt1 = new MyThread();

MyThread mt2 = new MyThread();

MyThread mt3 = new MyThread();

mt1.start() ;

mt2.start() ;

mt3.start() ;

}

}

现在的结果是一共买出了15张票,等于是每一个线程对象各自卖各自的5张票,这个时候的内存关系图如下:

范例:利用Runnable来实现多线程

package cn.mldn.demo;

class MyThread implements Runnable { // 线程的主体类

private int ticket = 5; // 一共5张票

@Override

public void run() { // 线程的主方法

for (int x = 0; x < 50; x++) {

if (this.ticket > 0) {

System.out.println("卖票,ticket = " + this.ticket--);

}

}

}

}

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt = new MyThread();

new Thread(mt).start();

new Thread(mt).start();

new Thread(mt).start();

}

}

现在使用继承Thread类也可以实现同样的功能。

package cn.mldn.demo;

class MyThread extends Thread { // 线程的主体类

private int ticket = 5; // 一共5张票

@Override

public void run() { // 线程的主方法

for (int x = 0; x < 50; x++) {

if (this.ticket > 0) {

System.out.println("卖票,ticket = " + this.ticket--);

}

}

}

}

public class TestDemo {

public static void main(String[] args) throws Exception {

MyThread mt = new MyThread();

new Thread(mt).start();

new Thread(mt).start();

new Thread(mt).start();

}

}

面试题:请解释多线程的两种实现方式及区别?分别编写程序以验证两种实现方式。

· 多线程的两种实现方式都需要一个线程的主类,而这个类可以实现Runnable接口或继承Thread类,不管使用何种方式都必须在子类之中覆写run()方法,此方法为线程的主方法;

· Thread类是Runnable接口的子类,而且使用Runnable接口可以避免单继承局限,以及更加方便的实现数据共享的概念。

转载于:https://www.cnblogs.com/guwenren/archive/2013/04/10/3013203.html

Java多线程(1)相关推荐

  1. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  2. Java多线程读取本地照片为二进制流,并根据系统核数动态确定线程数

    Java多线程读取图片内容并返回 1. ExecutorService线程池 2. 效率截图 3. 源码 1. ExecutorService线程池 ExecutorService线程池,并可根据系统 ...

  3. Java多线程,Thread,Runnable,Callable Task,Future<Task>,CompletionService

    一.Java多线程的方法 1. 继承 Thread 2. 实现 Runnable 3. 实现 Callable 可以有返回值 package com.test;import java.util.Arr ...

  4. 【收藏】Java多线程/并发编程大合集

    (一).[Java并发编程]并发编程大合集-兰亭风雨    [Java并发编程]实现多线程的两种方法    [Java并发编程]线程的中断    [Java并发编程]正确挂起.恢复.终止线程    [ ...

  5. 40个Java多线程问题总结

    (转) 这篇文章作者写的真是不错 40个问题汇总 1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓"知其然知其所 ...

  6. Java多线程编程实战:模拟大量数据同步

    背景 最近对于 Java 多线程做了一段时间的学习,笔者一直认为,学习东西就是要应用到实际的业务需求中的.否则要么无法深入理解,要么硬生生地套用技术只是达到炫技的效果. 不过笔者仍旧认为自己对于多线程 ...

  7. Java多线程学习处理高并发问题

    在程序的应用程序中,用户或请求的数量达到一定数量,并且无法避免并发请求.由于对接口的每次调用都必须在返回时终止,因此,如果接口的业务相对复杂,则可能会有多个用户.调用接口时,该用户将冻结. 以下内容将 ...

  8. Java多线程常见面试题及答案汇总1000道(春招+秋招+社招)

    Java多线程面试题以及答案整理[最新版]Java多线程高级面试题大全(2021版),发现网上很多Java多线程面试题都没有答案,所以花了很长时间搜集,本套Java多线程面试题大全,汇总了大量经典的J ...

  9. java多线程编程01---------基本概念

    一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...

  10. Java多线程的同步机制(synchronized)

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...

最新文章

  1. 赠书:一本书揭开 Spring Boot 技术内幕
  2. delphi oracle新建用户,Delphi 中如何用另外一个用户的身份来运行一人程序
  3. MySQL存储引擎中的MyISAM和InnoDB区别详解
  4. JavaScript 禁用键盘按钮
  5. 最好用的markdown编辑器推荐typora
  6. 安装alien,DEB与RPM互换
  7. (Q 2)netstat命令 检测TCP/IP 网络链接是否存在异常
  8. [SDOI2009]HH去散步(矩阵)
  9. 平安性格测试题及答案_面试要求做性格测试,该怎么做?
  10. css3组件实战--绚丽效果篇
  11. asio定时器深度探析
  12. java 8进制 前是 零,从零学java笔录-第6篇 进制之间转换
  13. mysql多源复制 知乎_技术分享 | MySQL 多源复制场景分析
  14. TNG-Hooks:有状态逻辑在标准函数中的重用和组合
  15. 自己的包增加为第三方包,使用Eclipse环境报Unresolved import错误(pycharm可用正常引用)...
  16. Java运行linux和windows系统命令
  17. WS2812B全彩灯板
  18. 一款小游戏集合自动脚本
  19. noip赛后【微笑】
  20. 全国绿地空间暴露数据集

热门文章

  1. 人脸识别三大经典算法_人脸识别系列四 | DeepID1算法
  2. mysql最大连接数查询_MYSQL 查看最大连接数和修改最大连接数
  3. 内网通 去广告 代码_一文秒懂Facebook广告投放常见专业术语
  4. mysql快捷创建数据库_mysql数据库快速入门(1)
  5. 前端javascripts基础知识点猴子吃桃
  6. python3.8.5依赖的urllib版本_Python(pip)-请求依赖警告:urllib3(1.9.1)或chardet(2.3.0)与受支持的版本不匹配...
  7. 解决An error occured while deploying the file. This probably means that the app contains ARM...
  8. 记录SQL server学习的存储过程的摘录
  9. ES6基础5(Promise)-学习笔记
  10. 入门训练 圆的面积 c语言