多线程 (基础入门)

本文深入解析了进程和线程的概念,阐述了它们在多任务处理中的作用,包括多进程和多线程的意义,以及线程调度模型。通过实例解释了Java中线程的创建和控制,包括线程的生命周期和状态。

1.1什么是进程?
1)什么是进程?
应用程序都需要先安装在本地的硬盘上。当启动程序的时候,系统找到这个程序的启动文件,把当前的这个程序加载到内存中,在内存中需要给当前的程序分配一段独立的运行空间。这片空间专门负责当前程序的运行。
不同的应用程序运行的过程,需要自己独立的运行空间,彼此之间不会相互的影响。
我们把每个独立应用程序在内存的独立空间称为当前应用程序运行的一个进程。
在这里插入图片描述
2)多进程有和意义?
A:充分利用了系统的资源
B:用户体验好

1.2什么是线程
1)什么是线程?
在一个进程中,每个独立的功能都需要独立的去运行,需要把当前这个进程划分成多个运行区域,每个独立的小区域(小单元)称为一个线程。
例如:360杀毒软件,同时既可以安全体检电脑又可以清理电脑中的垃圾。那么这里的安全体检是360杀毒软件中的一个线程,清理电脑中的垃圾也是一个线程。
线程:一个进程,可以同时执行多个任务,每个任务都是一条线程。它是位于进程中,负责当前进程中的某个具备独立运行资格的空间。

说明:进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行。一个进程要运行,至少要开启1条线程。然后如果要启动新的任务,就开启新的线程。每个线程有独立的内存空间,互不干扰。

进程和线程关系图解:
在这里插入图片描述
2)什么是多线程?
一个进程如果只有一条执行任务,则称为单线程程序。
一个进程如果有多条执行任务,也就是说在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。则称为多线程程序。

3)多线程有何意义?
提高了程序的执行效率。

4)线程是不是越多越好呢?
同一时间段内,如果线程过多,每个线程被切到的时候执行时间就变少了。所以开启的线程不是越多越好。

线程调度概念:假设我们的计算机只有一个CPU,那么CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?

线程有两种调度模型:
分时调度模型 :所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
抢占式调度模型 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 Java使用的是抢占式调度模型。

举例:QQ聊天,小明使用QQ和8个不同的人聊天。
对于8个人来讲:因为小明打字快,可以和8个人切换着窗口聊天,而另外8个人并没有感觉到,小明实际上在和8个人同时聊,而其他八个人觉得是在只和他们自己聊天。
对于小明来讲:是1个人和8个人聊天。这里小明就相当于CPU,其余八个人就相当于线程。

5)JVM运行时,是单线程还是多线程?
java程序想要运行,需要一个程序的入口:main函数。所以在JVM启动的时候,至少要启动1个线程,来执行main函数。
垃圾回收器与main函数并不是同一线程运行的。所以JVM在启动的时候,还会专门开辟一个线程,来执行垃圾回收器。
JVM启动,至少2条线程。一条是main函数,一条垃圾回收线程。
所以 JVM启动,至少2条线程。一条是main函数,一条垃圾回收线程。JVM是多线程运行。

1.3并行与并发
并行:同一时刻,多个线程同时运行。
并发:一段时间内,多个线程同时运行。

2.实现多线程
启动一个线程,需要调用系统资源,开辟内存空间,这些事情我们都不会。但是Java提供了一个API专门来完成这件事情。就是Thread类。

2.1方式一:继承Thread .
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。启动线程需要使用Thread类中的start()函数。在这个函数的底层调用了系统资源,开辟内存空间,同时调用了该线程的run函数。

2.1.3几个疑问?
1)为什么要继承Thread类?
在Java中使用Thread这个类对线程进行描述。而我们现在希望通过自己的代码操作线程,自己的代码应该需要和Thread类之间产生关系。这里我们采用的继承的关系。
当我们继承了Thread类之后,我们自己的类也就变成了线程类。我们自己的类就继承到了Thread类中的所有功能,就具备了操作线程的各种方法,并且自己的类就可以对线程进行各种操作(开启线程等)。而我们自己定义的类称为了一个线程类,主要是因为可以复写run方法。
2)为什么要复写run方法?
设计Thread这个API的人,在设计的时候,只设计了如何启动线程,至于线程要执行什么任务,他并不知道。所以,他这样设计:就是start启动线程之后,JVM会自动的调用run方法。

3)为什么要调用start而不是run?
只有调用start方法才会开启一个独立的新的空间。并在新的空间中自动去运行run方法。
4)start方法和run方法的区别?
run:只是封装线程任务。
start:先调用系统资源,在内存中开辟一个新的空间启动线程,再执行run方法。
5)同一个线程对象是否可以多次启动线程?
不能。如果同一个对象多次启动线程就会报如下异常。
在这里插入图片描述
2.1.4获取、设置线程名称
1.Thread类中的方法 String getName()就可以获取当前线程的名称。
可以通过Thread类中的setName()进行设置。

方式二:实现Runnable接口
第二种方式是创建一个类来实现Runnable接口,并实现Runnable接口中的run函数。
Runnable接口中只有一个函数
在这里插入图片描述
说明:1)我们发现,Thread类其实已经实现Runnable接口了,Thread类中的run方法就是实现来自Runnable接口的。
2)Runnable接口中,只有一个run方法,而run方法是用来封装线程任务的。
2.2.2实现步骤
A:自定义类,实现Runnable接口,这个类就是任务类;
B:实现run方法,run函数中书写的任务代码;
C:创建任务类的对象;
D:创建Thread类对象,并且把任务对象作为参数传递.
E:启动线程

A:定义一个类来实现Runnable接口,这个类是任务类

public class MyTask implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+": i");
			
		}
	}

}

测试代码:

public class ThreadDemo3 {

	public static void main(String[] args) {
		MyTask myTask = new MyTask();
		
		Thread thread = new Thread(myTask, "小米");
		Thread thread2 = new Thread(myTask, "jack");
		thread.start();
		thread2.start();
		
	}
}

2.2.3实现原理.
方式1:自定义类继承Thread类,重写run方法,调用start启动线程,会自动调用我们线程类中的run方法。
方式2:自定义类,实现Runnable接口,把任务对象传给Thread对
象。调用Thread对象的start方法,执行Thread的run。

2.2.4方式二的好处
A:避免了Java单继承的局限性;
B:把线程代码和任务的代码分离,解耦合(解除线程代码和任务的代码模块之间的依赖关系)。代码的扩展性非常好;

2.3线程的匿名内部类使用
使用匿名内部类的方式实现Runnable接口,重新复写Runnable接口中的run方法。

public static void main(String[] args) {
		//使用匿名内部类实现多线程 r表示任务类的对象
		Runnable r=new Runnable(){
			public void run() {
				for (int i = 0; i < 10; i++) {
					System.out.println(Thread.currentThread().getName()+"---"+i);
				}
			}
		};
		//创建线程类对象
		Thread t1=new Thread(r,"t1");
		Thread t2=new Thread(r,"t2");
		//启动线程
		t1.start();
		t2.start();
	}

2.4线程控制
使用Thread类中的sleep()函数可以让线程休眠。static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
说明:这个函数是静态的,使用线程类名调用。使用哪个线程调用就让哪个线程休眠。

/*
 * 演示线程的休眠
 * public static void sleep(long millis)
 */
//定义一个线程任务类
class SleepTask implements Runnable
{
	//实现run函数
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+"---"+i+new Date());
			//让线程睡一秒
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class SleepDemo {
	public static void main(String[] args) {
		//创建任务类对象
		SleepTask st = new SleepTask();
		//创建线程对象
		Thread t = new Thread(st,"兔子");
		//启动线程
		t.start();
	}
}

2.5线程的运行状态图(生命周期)
线程的运行状态是:创建—》启动—》运行—》死亡。
CPU执行线程有两个概念:
CPU的执行资格:在CPU执行的队列中等待。
CPU的执行权:当前持有CPU资源,正在被执行。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值