这里写目录标题
- 1, 关于多线程的介绍与执行
- 1.1什么是线程?什么是进程?
- 2.2进程和线程是什么关系
- 3.3主栈与其它栈的关系
- 4.4单核cpu和多核cpu
- 5.5run()和start()的作用和关系
- 6.6线程的个数判别
- 7.7实现线程的两种方法
- 8,run()方法和start()方法的区别
- 9线程的生命周期
- 10,获取线程的名字
- 11,线程的睡眠
- 12,线程的终止
- 13,线程的调度
- 14,线程合并
- 15,多线程并发环境下,数据安全问题
- 16,对synchronized的理解
- 17,扩大同步范围
- 18,synchronized出现在实例方法上
- 19,synchronized的三种写法
- 20,synchronized的面试题
- 21,死锁概述
- 22,开发中如何解决线程安全问题
- 二,线程这块还有那些方法
- 三,定时器
- 四,执行线程的第三种方法
- 六,布置线程作业实现交替作业![在这里插入图片描述](https://img-blog.csdnimg.cn/6ffae90e610344c3909dc5fd8d870960.png)
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的概述
六,布置线程作业实现交替作业
本文大量引用动力节点中讲义的内容
主要用于充当笔记,用于日常复习