Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【J2SE快速进阶】——Java多线程机制

【J2SE快速进阶】——Java多线程机制

作者头像
DannyHoo
发布于 2018-09-13 03:40:07
发布于 2018-09-13 03:40:07
28400
代码可运行
举报
文章被收录于专栏:Danny的专栏Danny的专栏
运行总次数:0
代码可运行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1336968

       学习Java中的线程时,自然而然地联想到之前学过的操作系统中处理器那一块的知识。

定义

       文章开头,先大概说一下程序、进程和线程的概念及其之间的关系。

       程序:程序就是一段静态的代码,或者一个可执行程序。

       进程:进程是程序的一次动态执行的过程,它对应着从代码加载、运行到结束的一次动态的执行过程。

       线程:比进程更小的执行单位,一个进程在执行过程中,可以产生多个线程,也就是多个分支。它是程序执行流的最小单位。

       来看一个小程序

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {
	public static void main(String[] args) {
		fun1();
	}
	public static void fun1(){
		System.out.println(fun2()+fun3());		
	}
	public static String fun2(){
		return "Hello ";
	}
	public static String fun3(){
		return "World!";
	}
}

    它的执行顺序如下,在main方法中,从①执行到⑥是一条线,并没有分支,这就是一个线程。

线程的实现

        当JVM执行到main方法时,就会启动一个线程,叫做主线程。如果main方法中还创建了其他线程,那么JVM就会在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源。

       Java中的线程是通过java.lang.Thread类来实现的,每一个Thread对象都代表一个新的线程。

       Java中实现线程有两种方法:

       1、继承Thread类,并且重写其run方法(用来封装整个线程要执行的命令),调用start方法启动线程。

       比如下面这个类T要实现线程,则代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CreateThreadTest{
	public static void main(String[] args) {
		T r=new T();
		r.start();   //T类的线程开始执行
		for(int i=0;i<100;i++)
		{
			System.out.println("主线程正在执行~~~~"+i);
		}
	}
}
class T extends Thread
{
	public void run(){//重写父类中的run方法
		for(int i=0;i<100;i++)
		{
			System.out.println("我创建的线程正在执行~~~~"+i);
		}
	}
}

       现在,这一个小程序一共有两个线程正在执行,一个是主线程,还有一个是T类创建的线程。

        2、实现Runnable接口

        还有一种方法就是让实现线程的类实现Runnable接口,实现Runnable接口中唯一的方法run(),然后把此类的实例当做Thread类的构造函数的参数,创建线程对象。

        例如上面的例子,还可以这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CreateThreadTest {
	public static void main(String[] args) {
		T r=new T();
		Thread t=new Thread(r);    //创建线程对象
		t.start();    //T类的线程开始
		for(int i=0;i<100;i++)
		{
			System.out.println("主线程正在执行~~~~"+i);
		}
	}
}
class T implements Runnable 
{
	public void run(){
		for(int i=0;i<100;i++)
		{
			System.out.println("我创建的线程正在执行~~~~"+i);
		}
	}
}

       两种方法的实质就是,都需要重写run方法,最终都是由Thread类的start方法启动线程。

温馨提示:因为Java中不支持多继承,所以实现线程时,一旦继承了Thread类,就无法再继承其他类了。但Java支持实现多个接口,所以推荐采用第二中方法,比较灵活。

多线程

多线程主要是为了同步完成多项任务,即同时执行多个线程。多线程把一个进程划分为多个任务,它们彼此独立地工作

       我们都知道,现在大部分操作系统比如Windows、Mac OS X、UnixLinux等,都是支持多线程的,但我们平时所说的多线程,并不意味着CPU在同时会处理多个线程,每个CPU在同一个时间点只会处理一个线程,只不过速度太快了,处理的时间极短,以至于我们可以认为它是在同一个时间段可以处理多个线程。所以只有当你的机器是“双核”甚至“多核”时,才能实现真正意义上的多线程。

       下面看一个多线程的例子:

        两个线程t1和t2的执行        

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ThreadTest {
	public static void main(String[] args) {
		Thread t1=new Thread(new T1());
		Thread t2=new Thread(new T2());
		t1.start();t2.start();
	}	
}
class T1 implements Runnable{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println("线程1正在执行中------"+i);
			if(i==9){
				System.out.println("线程1执行已结束------"+i);
				break;
			}
		}
	}
}
class T2 implements Runnable{
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println("线程2正在执行中------"+i);
			if(i==9){
				System.out.println("线程2执行已结束------"+i);
				break;
			}
		}
	}
}

         看下面的结果之前,先充分发挥你的大脑想一想到底结果应该是什么样子的~~

         执行结果:

       是不是跟您预测的不一样呢?如果不加线程的话,本来的结果应该前十行都是线程1在执行,线程1执行完后线程2才开始执行,线程调度算法使得每个线程执行一会进入等待状态,再去执行另一个线程。

线程中常用的方法

       如果亲手尝试过上面这个例子中,会发现这两个线程t1和t2谁先执行,谁后执行,谁执行多长时间等等这些都是不确定、不可控的。下面就说一下线程中常用到的几个方法。

       ★ void yield()方法

       在一个线程中,如果执行到yield()方法,那么这个线程就会让出CPU资源,暂停当前正在执行的线程对象,转而让CPU去执行其他具有相同优先级的线程。充分体现了线程乐于谦让的精神!

       例:i的值从0到99,每次输出线程实例的名字,当i是10的倍数时,执行yield方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public class TestYield {
	public static void main(String[] args) {
		MyThread thread1=new MyThread("thread1");
		MyThread thread2=new MyThread("thread2");
		thread1.start();thread2.start();
	}
}
class MyThread extends Thread{
	MyThread(String name){
		super(name);
	}
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(getName()+":"+i);
			if(i%10==0){
				yield();
			}
		}
	}
}

       从结果中的前几条输出可以发现,对thread1来说,每当i是10的倍数时,进程就会让出,thread2进程执行;thread2也是如此:

      注意: yield()方法会将当前运行的线程切换到可运行状态,但可能没有效果,因为实际中执行yield方法的线程还有可能被调度程序再次选中。

★ void sleep(long millis)方法和void sleep(long millis,int nanos)

       让线程休眠(暂停执行)指定的时间长度,参数millis的单位是毫秒,参数nanos的单位是纳秒。线程执行了sleep方法后就会进入阻塞状态,指定的时间段过后,线程进入可执行状态,等待被操作系统调度。

       例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.*;
public class TestSleep {
	public static void main(String[] args)
	{
		MyThread thread=new MyThread();
		thread.start();
	}
}
class MyThread extends Thread
{
	public void run()
	{
		while(true)
		{
			System.out.println("==="+new Date()+"===");
			try{
				sleep(1000);
			}catch(InterruptedException e){
				return;
			}
		}
	}
}

        看完代码您应该猜到结果了,每隔一秒都会输出当前时间,相当于一个定时器。这个程序一共有两个线程,主线程(即main方法)中出了执行thread线程就没有其他任务需要执行,所以这里可以看做只执行thread这一个线程,如果把例子中的sleep方法去掉,那么就会“唰唰唰”地不断输出当前时间(你的CPU风扇也会“唰唰唰”)。

         ★void join()方法

         上面说线程执行了yield方法时,会自动让出CPU资源,使状态由运行状态转换为可运行状态。join()方法可以说是恰恰相仿,当一个线程执行了join方法时,那么它就会一直执行下去直到这个线程结束。

        还是举例来说明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TestJoin {  
    public static void main(String[] args) throws InterruptedException {  
        Thread t1 = new Thread(new ThreadA());  
        Thread t2 = new Thread(new ThreadB());  
        t1.start();  
        t1.join(); // t1线程开始执行后,它将继续执行下去,直到t1线程结束,否则绝不让出CPU  
        t2.start();  
        t2.join(); // t2线程开始执行后,它将继续执行下去,直到t1线程结束,否则绝不让出CPU  
    }  
}
class ThreadA implements Runnable {   
    public void run() {  
        for (int i=0;i<10;i++) {  
            System.out.println("A线程正在运行~~~~" + i);  
        }   
    }  
}   
class ThreadB implements Runnable {   
	public void run() {  
        for (int i=0;i<10;i++) {  
            System.out.println("B线程正在运行~~~~" + i);  
        }   
    }   
} 

       先来设想一下,加入t1和t2两个线程启动后不执行join方法,那么CPU给它们分配的执行时间和顺序就不一定相同,如左下图;如果t1和t2执行了join方法,那么它们一旦开始执行,就将执行到底,如右下图。

为什么要用多线程

        最后一个问题,为什么要用多线程?

        在论坛里看到一个大牛的比喻:单线程就是牛逼老板从头到尾一个人做完,另开一个线程就是老板掰出一件事情叫一个小弟去做,这个小弟的进入会加快整个事情的进展,但有时可能会做起事来碍手碍脚。

       当然,想要更深地理解多线程的精髓所在,光靠学这些理论还是不够的,更重要的还是在实践和项目中去挖掘和思考。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015年03月09日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java多线程与并发
文章目录 线程和进程的区别 JDK选择长期版本 线程和进程的区别 基础 由来 区别 关系 代码查看主线程 Thread中的start方法和run方法的区别 效果图 代码 分析源码 没有可比性run和star Thread和Runnable是什么关系 本质 源码 代码演示 普通Thread.start()线程-效果图 MyThread 通过Thread类启动Runnable多线程-效果图 MyRunnalbe MyRunnableMain 如何实现处理线程的返回值 如何给run()方法传参 实现的方式主要有三
瑞新
2022/05/11
4910
Java多线程与并发
Java多线程详解3
Java多线程详解 Java线程:线程的调度-休眠 Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。 这里要明确的一点,不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。 线程休眠的目的是使线程让出CPU的最简单的做法之一,线程休眠时候,会将CPU资源交给其他线程,以便能轮换执行,当休眠一定时间后,线程会苏醒,进入准备状态等待执行。 线程休眠的方法是Thread.sleep(long millis)和Thread.sleep(l
Java帮帮
2018/03/15
7740
Java多线程基础
主线程挂了但是子线程还在继续执行,这并不会导致应用程序的结束。说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行(不会等执行完毕后再往下执行),这时 主线程和子线程是交替执行。
timerring
2023/05/07
2980
Java多线程基础
2-多线程
线程属于进程,一个进程可以包含多个线程,这就是多线程。(线程是进程中的一个独立执行单元)线程的创建开销相对于进程来说比较小,线程也支持并发性
Ywrby
2022/10/27
2210
Java多线程
例如打开你的计算机上的任务管理器,会显示出当前机器的所有进程,QQ,Chrome等,当QQ运行时,就有很多子任务在同时运行。比如,当你边打字发送表情,边好友视频时这些不同的功能都可以同时运行,其中每一项任务都可以理解成“线程”在工作。
用户10358987
2024/04/23
960
Java多线程
JDK源码分析 多线程
对于JDK源码分析的文章,仅仅记录我认为重要的地方。源码的细节实在太多,不可能面面俱到地写清每个逻辑。所以我的JDK源码分析,着重在JDK的体系架构层面,具体源码可以参考:http://www.cnblogs.com/skywang12345/category/455711.html。
Yano_nankai
2018/10/08
2820
【19】JAVASE-多线程专题【从零开始学JAVA】
Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
用户4919348
2024/05/25
1310
【19】JAVASE-多线程专题【从零开始学JAVA】
第36节:Java当中的线程
Java当中的线程,进程和线程的关系?进程就是线程吗?不是的。线程的运行,和方法。
达达前端
2019/07/03
3460
第36节:Java当中的线程
【day17】多线程基础
进程是在内存中执行的应用程序,而线程是进程中最小的执行单元,负责当前进程中程序的运行。一个进程中至少有一个线程,多个线程的应用程序称为多线程程序。
程序员波特
2024/12/26
830
【day17】多线程基础
多线程
3.开启线程不能用run()方法,用run()只是调用搞线程类的方法,并不是开启线程
xiaozhangStu
2023/05/04
2840
轻松掌握Java多线程 - 第一章:多线程入门
线程是操作系统能够进行运算调度的最小单位,也是程序执行流的最小单位。简单来说,线程就是一个单独的执行路径,它可以独立执行特定的代码片段。
程序猿梦工厂
2025/03/28
2700
轻松掌握Java多线程 - 第一章:多线程入门
Java多线程详解_java支持多线程
一个线程运行中,放弃了已经获取的CPU时间片,不再参与CPU时间片的抢占,此时线程处于挂起状态
全栈程序员站长
2022/09/23
1.4K0
Java多线程详解_java支持多线程
多线程基础
例子:单核CUP执行两件事,串行执行时间快,还是多线程执行快? 答:串行执行快。因为单核,执行的总时间一样,而多线程增加了线程切换的时间。
冬天vs不冷
2025/01/20
820
多线程基础
Java多线程
1.新建:程序使用new创建线程后,就是新建状态,jvm会为他分配内存,并初始化成员变量的值
用户11010370
2024/03/08
1170
控制线程
线程状态转换 线程控制基本方法 方法 功能 isAlive() 判断线程是否终止 getpriority() 获得线程的优先级数值 setpriority() 设置线程优先级数值 Thread.sle
mathor
2018/07/04
3500
【多线程】线程初体验
上节讲了下线程和进程的基础知识,但是对于Java来说,可能讨论线程的时间会更多些,所以接下来的一系列文章都是着重在讨论线程。
用户8902830
2021/08/12
2820
【多线程】线程初体验
Java多线程
两者区别:从上面的图中可以看出每个进程都会占用一定的内存,每个进程所占用的内存在操作系统中都是相互独立的,然后在线程当中,在同一块内存区域,每一个线程可以共享数据,所以线程之间的资源占用比较小
Gorit
2021/12/09
5600
Java多线程
【Java学习笔记之三十四】超详解Java多线程基础
前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧。 正文 线程与进程 1 线程:进程中负责程序执行的执行单元 线程本身依靠程序进行运行 线程是程序中的顺序控制流,只能使用分配给程序的资源和环境 2 进程:执行中的程序 一个进程至少包含一个线程 3 单线程:程序中只存在一个线程,实际上主方法就是一个主线程 4 多线程:在一个程序中运行多个任务 目的是更好地使用CPU资源 线程的实现 继承Thread类
Angel_Kitty
2018/04/09
9000
【Java学习笔记之三十四】超详解Java多线程基础
进程与线程+多线程优势
但是进程申请资源对系统的性能影响较大,涉及内存和文件资源,处理一个事情有一份资源就够了。
用户11162265
2024/11/20
930
进程与线程+多线程优势
Java多线程与并发
进程是资源分配的基本单位,所有与进程有关的资源都记录在进程控制块PCB中,以表示进程拥有这些资源或者正在使用它们,进程也是抢占处理机的调度单位,它拥有完整的虚拟内存地址空间,当进程发生调度时,不同的进程拥有不同的地址空间,而同一进程内的不同线程共享同一地址空间。与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其它线程共享进程的资源。
ha_lydms
2023/08/10
2000
Java多线程与并发
相关推荐
Java多线程与并发
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验