??
??????
?个线程就是?个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码,main()?般被称为主线程(Main Thread)。
首先, “并发编程” 成为 “刚需”.
单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源.
有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做?些其他的工作, 也需要用到并发编程. 其次,
虽然多进程也能实现 并发编程, 但是线程比进程更轻量.
创建线程比创建进程更快.
销毁线程比销毁进程更快.
调度线程比调度进程更快.
最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine)
关于线程池我们后面再介绍. 关于协程的话题我们此处暂时不做过多讨论.
线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了?些API供用户使用(例如Linux的pthread库) 例如:Java标准库Thread的类可以视为是对操作系统提供的API进行了进?步的抽象和封装.
继承Thread来创建?个线程类,直接使用this就表示当前线程对象的引用
class MyThread extends Thread {
@Override
public void run() {
System.out.println("这?是线程运?的代码");
}
}
public class Test {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
实现Runnable接口,this表示的是 MyRunnable 的引用.需要使用Thread.currentThread()
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这?是线程运?的代码");
}
}
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
public class Test {
public static void main(String[] args) {
// 使?匿名类创建 Thread ?类对象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使?匿名类创建 Thread ?类对象");
}
};
}
}
public class Test {
public static void main(String[] args) {
// 使?匿名类创建 Runnable ?类对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使?匿名类创建 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 类是 JVM 用来管理线程的?个类,换句话说,每个线程都有?个唯?的 Thread 对象与之关联。而Thread 类的对象就是用来描述?个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
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();
}
}
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
也是我们比较熟悉?组方法,有?点要记得,因为线程的调度是不可控的,所以,这个方法只能保证
实际休眠时间是大于等于参数设置的休眠时间的。
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());
}
}
关注 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());;
}
}
关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
public static void main(String[] args) {
final Object object = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
System.out.println("hehe");
}
}
}, "t2");
t2.start();
}
使用jconsole可以看到t1的状态是TIMED_WAITING,t2的状态是BLOCKED
结论:
想给出?个线程安全的确切定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。
线程调度是随机的,这是线程安全问题的罪魁祸首,随机调度使?个程序在多线程环境下,执行顺序存在很多的变数.程序猿必须保证在任意执行顺序下,代码都能正常工作.
代码实现时不会受到其它线程的穿插执行,这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。
?个线程对共享变量值的修改,能够及时地被其他线程看到.
Java内存模型(JMM):Java虚拟机规范中定义了Java内存模型.目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到?致的并发效果.
什么是代码重排序
?段代码是这样的:
1.去前台取下U盘
2. 去教室写10分钟作业
3. 去前台取下快递 如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按1->3->2的方式执行,也是没问 题,可以少跑?次前台。这种叫做指令重排序 编译器对于指令重排序的前提是"保持逻辑不发生变化".这?点在单线程环境下比较容易判断,但是 在多线程环境下就没那么容易了,多线程的代码执行复杂程度更高,编译器很难在编译阶段对代码的 执行效果进行预测,因此激进的重排序很容易导致优化后的逻辑和之前不等价. 重排序是?个比较复杂的话题,涉及到CPU以及编译器的?些底层工作原理,此处不做过多讨论
如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见*!!!💓 💓 💓*