JAVA语言多线程的执行

13 篇文章 1 订阅
5 篇文章 2 订阅

1, 关于多线程的介绍与执行

1.1什么是线程?什么是进程?

简单来说:

  • 如果说进程是一个应用程序(一个进程是一个软件)
  • 线程则是一个进程中的执行场景/执行单元
  • 一个进程可以启动多个线程

注意:
对于java程序来说,当在DOS命令窗口中输入;java HelloWorld 回车之后。

  • 会先启动JVM,而JVM就是一个进程。
  • JVM再启动一个主线程调用main方法。
  • 同时再启动一个垃圾回收线程负责看护,回收垃圾。
  • 现在的java程序中至少有两个线程并发。
  • 一个是垃圾回收线程,一个是执行main方法的主线程

2.2进程和线程是什么关系

  • 进程是操作系统资源分配的最小单元,线程操作系统任务分配的最小单元
    举个例子:
    1,一个国家: 进程
    则政府官员是线程,人民是线程
    2,一个工厂: 进程
    则工厂中的每一个工作的工人是线程,每一台工作的机器是线程
  • 总结: 线程隶属于进程,每个进程有许多线程

注意:

  • 进程国家和进程工厂的内存不共享。
    例如:
    1,守望先锋是一个进程
    2,网易云音乐也是一个进程
    这两个进程相互独立,不共享资源
  • 在java语言中:线程国家和线程工厂,堆内存和方法区内存共享但是栈内存独立,一个线程一个栈。
  • 假设启动10个线程,会有10个栈空间,每个栈与每个栈之间互不干扰,各自执行各自的,这就是多线程并发
  • 这就如同去售票点买车票,每个售票窗口都是一个线程。张三到窗口1买票,李四到窗口2买票…李四不需要等张三买完票之后再买票,可以直接在窗口2买,这样大大提高了效率多线程并发的原理正是如此,通过多个线程同时执行提高了程序的执行效率。
  • JAVA中之所以有多线程机制,目的就是为了提高程序的处理效率

3.3主栈与其它栈的关系

使用了多线程机制之后,随着main方法结束,主线程结束了,主栈空了,其它栈(线程)可能还在压栈弹栈因为栈的空间是相互独立的。

  • 压栈(执行方法时,方法进栈也就是压栈)
  • 弹栈(当该方法执行完毕时,则方法出栈也就是弹栈)
    在这里插入图片描述
    多线程并发的原理如上图所示:
    t1线程执行t1的
    main线程执行main线程的
    t1不会影响main,main也不会影响t1这叫做真正的多线程并发

4.4单核cpu和多核cpu

对于单核cpu来说,不能做到真正的多线程并发,因为单核cpu表示只有一个大脑,但可以给人带来一种多线程并发的感觉,对于单核cpu来说,在某一个时间点上实际上只能处理一件事情,但是由于cpu的处理速度极快,多个线程之间频繁切换执行(这里涉及到程序运行时控制台的争夺)所以就会给人带来一种多个线程同时执行的感觉

  • 这就好比我们看的动画片,看上去是动态的其实是静态的画面逐帧显示,只是显示的速率很快从而给了我们一种动态的感觉

多核cpu则可以实现线程的并发进行
通常4核cpu可以同时实现4个线程同时进行而8核cpu可以实现8个线程同时进行
现如今电脑以8核cpu为主

5.5run()和start()的作用和关系

start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,并且让开启的线程去执行 run () 方法 中的线程任务,这段代码任务完成之后,瞬间就结束了。
run()方法不能真正的去启动线程,而是由一个叫main的主线程先执行 start() 方法再去调用的 run () 方法,当线程开始执行时,Java虚拟机会去调用该线程的run()方法

6.6线程的个数判别

例:单个线程

package com.多线程的执行.java.thread
/*以下程序除垃圾回收线程之外。有1个线程(因为程序只有一个栈)*/
public class ThreadTest01
{
	public static void main(String[]args){
		System.out.println("main begin");
		m1();
		System.out.println("main over");
	 }
	private static void m1(){
		System.out.println("m1 begin");
		m2();
		System.out.println("m1 over");
	 }
    private static void m2(){
		System.out.println("m2 begin");
		System.out.println("m2 over");
	}
	m1,m2,main都是方法
}

在这里插入图片描述

例:多个线程

package com.多线程的执行.java.thread
/*以下程序除垃圾回收线程之外。有1个线程(因为程序只有一个栈)*/
public class ThreadTest02
{
	public static void main(String[]args){
		//这里是main方法,这里的代码属于主线程,在主栈中运行。
		//新建一个分支线程对象
		MyThread myThread = new MyThread();
		//启动线程
		myThread.start();
		//这里的代码还是运行在主线程中
		for(int i = 0;i < 1000; i++){
			System.out.println("主线程--->" + i);
		}
	}
}
class MyThread extends Thread{
	@Override
	public void run(){
		//编写程序,这段程序运行在分支线程中(分支栈)
		for(int i =0; i< 1000; i++){
			System.out.println("分支线程--->" + i);
		}
	}
}

7.7实现线程的两种方法

在这里插入图片描述

第一种: 编写一个类,直接继承java.lang.Thread,重写run方法

package com.多线程的执行.java.thread
public class ThreadTest
{
 	public static void main(String[]args){
 		MyThread myThread = new MyThread();
 		myThread.start();
 		for(int i = 0;i < 1000; i++){
 			System.out.println("主线程--->" + i);
  		}
    }
}  
class MyThread extends Thread{
 	@Override
  	public void run(){
 		for(int i =0; i< 1000; i++){
  			System.out.println("分支线程--->" + i);
  		}
  	}
} 

第二种: 编写一个类,实现java.lang.Runnable接口,实现run()方法

package com.多线程的执行.java.thread
public class ThreadTest
{
  	public static void main(String[]args){
   		MyThread myThread = new MyThread();
   		Thread th=new Thread(mythread);
   		th.start();
   		for(i=0;i<1000;i++)
   			System.out.println("主线程"+i);
   	}
}
class MyThread implements Runnable
{
  	public void run(){
   		for(int i =0; i< 1000; i++){
      		System.out.println("分支线程--->" + i);
      	}
    }
}

Runnable接口相比于Thread的优点

  • 使用Runnable接口后还可以继承其它接口和类
  • 而使用Thread类的话则不能再继承其它类了(具有局限性)

8,run()方法和start()方法的区别

在这里插入图片描述

start()方法开辟分支栈空间,开辟后分支栈中的run()方法开始运行

9线程的生命周期

  • 1,新建状态:(new)当线程对象创建以后即进入了新建状态如:Thread t= new MyThread();
  • 2,就绪状态:当调用线程对象的start()方法(t.start();),线程即进入就绪状态,就绪状态的线程又叫做可运行状态,表示当前线程具有抢夺cpu时间片的权利(cpu时间片就是执行权)。随时等待cpu调度执行
  • 3,运行状态:run()方法的执行标志着这个线程进入运行状态,当之前占有的cpu时间片用完之后,会重新回到就绪状态继续抢夺cpu时间片,当再次抢到cpu时间之后,会重新进入run方法会接着上一次的代码继续往下执行
  • 4,阻塞状态:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。
    1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
    2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
    3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  • 5,死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
    在这里插入图片描述

10,获取线程的名字

11,线程的睡眠

12,线程的终止

13,线程的调度

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14,线程合并

在这里插入图片描述

在这里插入图片描述

15,多线程并发环境下,数据安全问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
专业术语叫线程同步,其实就是线程排队执行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

先创建一个账户类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16,对synchronized的理解

在这里插入图片描述

17,扩大同步范围

在这里插入图片描述
在这里插入图片描述

18,synchronized出现在实例方法上

在这里插入图片描述

在这里插入图片描述

19,synchronized的三种写法

在这里插入图片描述

20,synchronized的面试题

面试题1:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
面试题2:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

面试题3:将上述代码中MyClass1类给定义两个对象分别为mc1和mc2,并将它们传入MyThread1类中的构造方法的参数中
然后问你doSome()方法和doOther()方法之间是否会产生等待

答:不会产生等待,因为MyClass1类的对象为两个,两把锁不会产生等待

面试题4:将doSome和doOther方法均加上static静态修饰,则会产生等待的效果,因为加上static方法修饰后表示类锁,无论你创建了多少个对象,类锁只有一把

21,死锁概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

22,开发中如何解决线程安全问题

在这里插入图片描述

二,线程这块还有那些方法

在这里插入图片描述

1,守护线程概述

在这里插入图片描述
调用setDaemon()方法将线程设置为守护线程
在这里插入图片描述
在这里插入图片描述

三,定时器

1,定时器概述

在这里插入图片描述

2,实现定时器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四,执行线程的第三种方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五,notify和wait的概述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六,布置线程作业实现交替作业在这里插入图片描述

本文大量引用动力节点中讲义的内容
主要用于充当笔记,用于日常复习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值