视频网站开发平台,网站做的好的公司,网站设计公司网站制作费用,dw网站制作模板多线程 一.认识线程#xff08;Thread#xff09;1. 1) 线程是什么1. 2) 为啥要有线程1.3) 进程和线程的区别标题1.4) Java的线程和操作系统线程的关系 二.创建线程方法1:继承Thread类方法2:实现Runnable接口方法3:匿名内部类创建Thread子类对象标题方法4:匿名内部类创建Runn… 多线程 一.认识线程Thread1. 1) 线程是什么1. 2) 为啥要有线程1.3) 进程和线程的区别标题1.4) Java的线程和操作系统线程的关系 二.创建线程方法1:继承Thread类方法2:实现Runnable接口方法3:匿名内部类创建Thread子类对象标题方法4:匿名内部类创建Runnable子类对象方法5:lambda表达式创建Runnable子类对象 三.Thread类及其方法3.1Thread的常见构造方法3.2Thread的几个常见属性3.3获取当前线程引用3.4休眠当前线程 四:线程的状态4.1线程的所有状态4.2线程状态和状态转移的意义4.3观察线程的状态和转移示例1:示例2: 五:多线程带来的的风险-线程安全(重点)5.1线程安全的概念5.2线程不安全的原因5.3线程的几大特性5.3.1:原子性5.3.2:可见性5.3.3:指令重排序
一.认识线程Thread
1. 1) 线程是什么
⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码,main()⼀般被称为主线程Main Thread。
1. 2) 为啥要有线程
首先, “并发编程” 成为 “刚需”. 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源. 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做⼀些其他的工作, 也需要用到并发编程. 其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量. 创建线程比创建进程更快. 销毁线程比销毁进程更快. 调度线程比调度进程更快.
最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine) 关于线程池我们后面再介绍. 关于协程的话题我们此处暂时不做过多讨论.
1.3) 进程和线程的区别
进程是包含线程的. 每个进程至少有⼀个线程存在即主线程。进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.进程是系统分配资源的最小单位线程是系统调度的最小单位。⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带走(整 个进程崩溃)
标题1.4) Java的线程和操作系统线程的关系
线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了⼀些API供用户使用(例如Linux的pthread库) 例如:Java标准库Thread的类可以视为是对操作系统提供的API进行了进⼀步的抽象和封装.
二.创建线程
方法1:继承Thread类
继承Thread来创建⼀个线程类,直接使用this就表示当前线程对象的引用
class MyThread extends Thread { Overridepublic void run() {System.out.println(这⾥是线程运⾏的代码);}
}
public class Test {public static void main(String[] args) {MyThread t new MyThread();t.start();}
}方法2:实现Runnable接口
实现Runnable接口,this表示的是 MyRunnable 的引用.需要使用Thread.currentThread()
class MyRunnable implements Runnable {Overridepublic void run() {System.out.println(这⾥是线程运⾏的代码);}
}
public class Test {public static void main(String[] args) {Thread t new Thread(new MyRunnable());t.start();}
}方法3:匿名内部类创建Thread子类对象
public class Test {public static void main(String[] args) {// 使⽤匿名类创建 Thread ⼦类对象Thread t1 new Thread() {Overridepublic void run() {System.out.println(使⽤匿名类创建 Thread ⼦类对象);}};}
}标题方法4:匿名内部类创建Runnable子类对象
public class Test {public static void main(String[] args) {// 使⽤匿名类创建 Runnable ⼦类对象Thread t2 new Thread(new Runnable() {Overridepublic void run() {System.out.println(使⽤匿名类创建 Runnable ⼦类对象);}});}
}方法5:lambda表达式创建Runnable子类对象
public class Test {public static void main(String[] args) {// 使⽤匿名类创建 Runnable ⼦类对象// 使⽤ lambda 表达式创建 Runnable ⼦类对象Thread t3 new Thread(() - System.out.println(使⽤匿名类创建 Thread ⼦类对象));Thread t4 new Thread(() - {System.out.println(使⽤匿名类创建 Thread ⼦类对象);});}
}三.Thread类及其方法
Thread 类是 JVM 用来管理线程的⼀个类换句话说每个线程都有⼀个唯⼀的 Thread 对象与之关联。而Thread 类的对象就是用来描述⼀个线程执行流的JVM 会将这些 Thread 对象组织起来用于线程调度线程管理。
3.1Thread的常见构造方法 Thread t1 new Thread();
Thread t2 new Thread(new MyRunnable());
Thread t3 new Thread(这是我的名字);
Thread t4 new Thread(new MyRunnable(), 这是我的名字);3.2Thread的几个常见属性 ID是线程的唯⼀标识不同线程不会重复 名称是各种调试工具用到 状态表示线程当前所处的⼀个情况下面我们会进⼀步说明 优先级高的线程理论上来说更容易被调度到
关于后台线程需要记住⼀点JVM会在⼀个进程的所有非后台线程结束后才会结束运行。 是否存活即简单的理解为run方法是否运行结束了
public class Test {public static void main(String[] args) {Thread thread new Thread(() - {for (int i 0; i 10; i) {try {System.out.println(Thread.currentThread().getName());Thread.sleep(1 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() : 我即将死去)});System.out.println(Thread.currentThread().getName() : ID: thread.getId());System.out.println(Thread.currentThread().getName() : 名称: thread.getName());System.out.println(Thread.currentThread().getName() : 状态: thread.getState());System.out.println(Thread.currentThread().getName() : 优先级: thread.getPriority());System.out.println(Thread.currentThread().getName() : 后台线程: thread.isDaemon());System.out.println(Thread.currentThread().getName() : 活着: thread.isAlive());System.out.println(Thread.currentThread().getName() : 被中断: thread.isInterrupted());thread.start();}
}
3.3获取当前线程引用 public class ThreadDemo {public static void main(String[] args) {Thread thread Thread.currentThread();System.out.println(thread.getName());}
}3.4休眠当前线程
也是我们比较熟悉⼀组方法有⼀点要记得因为线程的调度是不可控的所以这个方法只能保证 实际休眠时间是大于等于参数设置的休眠时间的。
public class ThreadDemo {public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(3 * 1000);System.out.println(System.currentTimeMillis());}
}四:线程的状态
4.1线程的所有状态
NEW:安排了工作,还未开始行动RUNNABLE:可工作的.又可以分成正在工作中和即将开始工作.BLOCKED:这几个都表示排队等着其他事情WAITING:这几个都表示排队等着其他事情TIMED_WAITING:这几个都表示排队等着其他事情TERMINATED:工作完成了
4.2线程状态和状态转移的意义 4.3观察线程的状态和转移
示例1:
关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换
public class ThreadStateTransfer {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {for (int i 0; i 1000_0000; i) {}}, 李四);System.out.println(t.getName() : t.getState());;t.start();while (t.isAlive()) {System.out.println(t.getName() : t.getState());;}System.out.println(t.getName() : t.getState());;}
}示例2:
关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
public static void main(String[] args) {final Object object new Object();Thread t1 new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}, t1);t1.start();Thread t2 new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {System.out.println(hehe);}}}, t2);t2.start();}使用jconsole可以看到t1的状态是TIMED_WAITING,t2的状态是BLOCKED
结论:
BLOCKED表示等待获取锁,WAITING和TIMED_WAITING表示等待其他线程发来通知.TIMED_WAITING线程在等待唤醒但设置了时限;WAITING线程在无限等待唤醒
五:多线程带来的的风险-线程安全(重点)
5.1线程安全的概念
想给出⼀个线程安全的确切定义是复杂的但我们可以这样认为如果多线程环境下代码运行的结果是符合我们预期的即在单线程环境应该的结果则说这个程序是线程安全的。
5.2线程不安全的原因
线程调度是随机的,这是线程安全问题的罪魁祸首,随机调度使⼀个程序在多线程环境下,执行顺序存在很多的变数.程序猿必须保证在任意执行顺序下,代码都能正常工作.
5.3线程的几大特性
5.3.1:原子性
代码实现时不会受到其它线程的穿插执行,这样就保证了这段代码的原子性了。 有时也把这个现象叫做同步互斥表示操作是互相排斥的。
5.3.2:可见性
⼀个线程对共享变量值的修改能够及时地被其他线程看到.
Java内存模型(JMM):Java虚拟机规范中定义了Java内存模型.目的是屏蔽掉各种硬件和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到⼀致的并发效果.
线程之间的共享变量存在主内存(Main Memory).每⼀个线程都有自己的工作内存(Working Memory)当线程要读取⼀个共享变量的时候,会先把变量从主内存拷贝到工作内存,再从工作内存读取数据.当线程要修改⼀个共享变量的时候,也会先修改工作内存中的副本,再同步回主内存,由于每个线程有自己的工作内存,这些工作内存中的内容相当于同⼀个共享变量的副本.此时修改线程1的工作内存中的值,线程2的工作内存不⼀定会及时变化.
5.3.3:指令重排序
什么是代码重排序 ⼀段代码是这样的 1.去前台取下U盘 2. 去教室写10分钟作业 3. 去前台取下快递 如果是在单线程情况下JVM、CPU指令集会对其进行优化比如按1-3-2的方式执行也是没问 题可以少跑⼀次前台。这种叫做指令重排序 编译器对于指令重排序的前提是保持逻辑不发生变化.这⼀点在单线程环境下比较容易判断,但是 在多线程环境下就没那么容易了,多线程的代码执行复杂程度更高,编译器很难在编译阶段对代码的 执行效果进行预测,因此激进的重排序很容易导致优化后的逻辑和之前不等价. 重排序是⼀个比较复杂的话题,涉及到CPU以及编译器的⼀些底层工作原理,此处不做过多讨论 如果觉得文章不错期待你的一键三连哦你个鼓励是我创作的动力之源让我们一起加油顶峰相见* *