🧡💛💚💙💜🤎💗🧡💛💚💙💜🤎💗
感谢各位一直以来的支持和鼓励 制作不易
🙏🙏🙏 求点赞 👍 👍 👍 ➕ 收藏 ⭐ ⭐ ⭐ ➕ 关注✅✅✅
一键三连走起 ! ! !
🧡💛💚💙💜🤎💗 🧡💛💚💙💜🤎💗
【友情链接】➡➡▶线程入门学习,线程同步机制 和 线程等待与唤醒机制
【友情链接】➡➡▶线程入门学习,线程池 & Lambda表达式
一、并发和并行
并发:指两个或多个事件在同一个时问段内发生。(交替执行)
可以理解为在一段时间内,cpu在多个任务之间交替执行;就好比小屁孩吃苹果和桃,左手拿苹果,右手拿桃,先吃一口苹果,再吃一口桃,然后又回来吃一口苹果,交替着吃苹果和桃,这也就是交替执行
并行:指两个或多个事件在同一时刻发生(同时发生).(并行执行)
大家还记得小时候老师让我们用 “一边…一边…” 来造句吗?哈哈,小北来造个句,小明一边看电视一边听歌。那么这里的看电视和听歌就是在同时进行着,,,这也就是并行执行。
二、进程和线程
(1)进程
进程是指内存中正在运行的程序
- 进程是系统进行资源分配和调用的独立单位
- 每一个进程都有它自己的内存空间和系统资源
- 进程指当前正在执行的程序,代表一个应用程序在内存中的执行区域
(2)线程
线程是进程中单个顺序控制流,是一条执行路径
- 线程是进程中的一个执行控制单元,执行路径。
- 单线程:一个进程如果只有一条执行路径,则称为单线程
- 多线程:一个进程如果有多个执行路径,则称为多线程
注意:
进程只是负责开辟内存空间的,线程才是负责执行代码逻辑的执行单元。
三、线程具有随机性
因为在某一个时刻CPU只能执行一个程序,所以当多个程序同时执行时,其实就是CPU在做着快速的切换完成的,这样就可能造成执行代码打印数据时,数据的不规律。详细分析一下线程的执行步骤:Java虚拟机执行main()方法时,首先会找操作系统(OS)开辟一条main()方法通向CPU的路径,这个路径就是main线程(或者“主线程”),那么CPU就可以通过这条路经来执行main()方法,而在main()方法中,通过创建Thread类的子类对象,来调用执行start()方法时,会开辟一条新的通向CPU的新路径来执行run()方法,此时对于CPU来说就有了两条路经了,所以CPU就会随机选择一条路劲执行,那么这样就会导致线程的随机执行。
四、线程的调度
- 1.分时调度
程序中的所有线程会轮流使用CPU的使用权,CPU平均分配每个线程占用CPU的时间。 - 2.抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么CPU就会随机选择一个线程执行(这也就是线程的随机性), Java使用的就是抢占式调度。
五、多线程的实现原理
六、通过Thread类实现多线程
(1)Thread类实现多线程的方式步骤:
- 1.自定义一个类继承Thread类
- 2.在类中重写run方法
- 3.创建类的对象
- 4.启动线程
(2)重写run方法的原因
- run方法中的内容是用来封装被线程执行的代码
(3)run()方法和start()方法的区别:
- run():用来封装线程执行的代码,可以直接调用
- start():该方法用来启动线程,也就是开辟一个新的栈内存,然后再由JVM调用此线程的run()方法
(4)Thread类中获取线程名称的方法
1.使用Thread类中的getName()方法
string getName():返回该线程的名称。
示例·代码:
//创建一个MyThread类
public class MyThread extends Thread {
//重写run()方法
@Override
public void run() {
//调用getName()方法获取线程名称
String mtName =getName();
System.out.println(mtName);
}
}
//创建一个test测试类
public class test {
public static void main(String[] args) {
//创建MyThread类对象
MyThread myThread = new MyThread();
//调用start()方法,开启线程
myThread.start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
测试结果:
2.获取当前正在执行的线程
static Thread currentThread():返回对当前正在执行的线程对象的引用。
示例代码:
public class MyThread extends Thread {
//重写run()方法
@Override
public void run() {
//获取线程
Thread currentThread = Thread.currentThread();
System.out.println(currentThread);
//打印线程名
System.out.println(currentThread.getName());
}
}
//创建一个test测试类
public class test {
public static void main(String[] args) {
//创建MyThread类对象
MyThread myThread = new MyThread();
//调用start()方法,开启线程
myThread.start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
测试结果:
(5)Thread类中设置线程名称的方法
1.使用Thread类中的方法setName()
void setName(String name) 改变线程名称
⒉创建一个带参数的构造方法,参数传递线程的名称,调用父类(Thread)的带参构造方法,把线程名称传递给父类,让父类给子线程起一个名字
Thread(string name) 分配新的Thread对象。
示例代码:
//创建MyThread类继承Thread类
public class MyThread extends Thread {
public MyThread(){}
public MyThread(String s) {
super(s);//将名称传给父类,让父类给线程起名
}
//重写run()方法
@Override
public void run() {
//打印线程名
System.out.println(Thread.currentThread().getName());
}
}
//创建一个test测试类
public class test {
public static void main(String[] args) {
//创建MyThread类对象
MyThread myThread = new MyThread();
//设置线程名称
myThread.setName("我是自定义线程hello");
//调用start()方法,开启线程
myThread.start();
new MyThread("我是自定义线程world").start();
}
}
测试结果:
七、通过Runnable实现多线程
java.lang包下的Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。而类中必须定义一个名为 run的无参数方法,run方法
Thread类的构造方法有
Thread( Runnable target) 分配新的Thread 对象。
Thread ( Runnoble target,string name) 分配新的Thread对象。
实现步骤:
- 1.创建一个类实现Runnable接口
- 2.在类中重写Runnable接口的run()方法,设置线程的任务
- 3.创建实现类的对象
- 4.创建Thread对象,把实现类的对象作为构造方法的参数
- 5.调用Thread类的start()方法,启动线程,执行run()方法
示例代码:
//1.创建一个类实现Runnable接口
public class RunnableImp implements Runnable {
//2.在类中重写Runnable接口的run()方法,设置线程的任务
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
public class test {
public static void main(String[] args) {
//3.创建实现类的对象
RunnableImp runnableImp = new RunnableImp();
//4.创建Thread对象,把实现类的对象作为构造方法的参数
Thread thread = new Thread(runnableImp);
//5.调用Thread类的start()方法,启动线程,执行run()方法
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
测试结果: 抢占执行
八、两种实现多线程的方式的区别
java中的继承都是单继承,不利于资源共享,所以当一个类继承Thread类来实现多线程时,就不适合资源共享。但如果一个类通过实现Runable接口来实现多线程,同时该实现类还可以实现其他的接口,或继承其他的类,那么就很容易实现资源共享,大大提高程序的效率。
实现Runnable接口比继承Thread类所具有的优势:
- 1.通过Runnable接口实现多线程,适合有多个相同的程序代码的线程去共享同一个资源。
- 2.通过Runnable接口实现多线程,可以避免java中单继承的局限性。
- 3.通过Runnable接口实现多线程,可以增加程序的健社性(扩展性),降低程序的耦合性(实现解耦操作),代码可以被多个线程共享,代码和线程又相互独立。
注意: 通过Runnable接口实现多线程,将设置线程任务和开辟一个新的线程分离开了(这也就是解耦),在实现类中重写run()方法来设置线程任务,通过Thread类的对象,调用start()方法来开辟一个新的线程
- 4.线程池中只能放入实现Runnable或Callable类线程,而不能直接放入继承Thread的类。
九、通过匿名内部类实现线程的创建
匿名内部类的详细介绍,链接:
【小白学Java】D18》》》匿名类&匿名对象&内部类
测试代码:
public class Task {
public static void main(String[] args) {
/**
* 匿名内部类使用格式;
* new 父类/接口(){
* //重写父类/接口中的方法
* };
* */
//Thread类实现多线程
new Thread(){
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"Thread");
}
}
}.start();
//Runnable接口实现多线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"Runnable");
}
}
}).start();
}
}
测试结果: