一课掌握Java并发编程精髓(完结13章)
分享一套Java课程——一课掌握Java并发编程精髓(完结13章),附源码+PDF课件下载。
并发编程
1.多线程
Java 是最先支持多线程的开发的语言之一,Java 从一开始就支持了多线程能力。由于现在的 CPU 已经多是多核处理器了,是可以同时执行多个线程的。
多线程优点
多线程技术使程序的响应速度更快 ,可以在进行其它工作的同时一直处于活动状态,程序性能得到提升。
性能提升的本质 就是榨取硬件的剩余价值(硬件利用率)。
并行与并发
单核 cpu 下,线程实际是串行执行的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片,分给不同的线程使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是同时运行的。
总结为一句话就是:微观串行,宏观并行,一般会将这种线程轮流使用 cpu的做法称为并发,concurrent。
Java 内存模型(Java Memory Model,JMM)规范了 Java 虚拟机与计算机内存是如何协同工作的。Java 虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内存模型——又称为 Java 内存模型。
JVM 主内存与工作内存
Java 内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。
这里的工作内存是 JMM 的一个抽象概念,也叫本地内存,其存储了该线程读/写共享变量的副本。
线程常用的方法
start() : 启动当前线程, 调用当前线程的run()方法。
run() : 通常需要重写Thread类中的此方法, 将创建的线程要执行的操作声明在此方法中。
currentThread() : 静态方法, 返回当前代码执行的线程。
getName() : 获取当前线程的名字。
setName() : 设置当前线程的名字。
yield() : 释放当前CPU的执行权。
join() : 在线程a中调用线程b的join(), 此时线程a进入阻塞状态, 知道线程b完全执行完以后, 线程a才结束阻塞状态。
stop() : 已过时. 当执行此方法时,强制结束当前线程.
sleep(long militime) : 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态。
isAlive() :判断当前线程是否存活。
wait()和 sleep()方法有什么区别
sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点在于如果线程持有某个对象的监视器,sleep 方法不会放弃这个对象的监视器,wait 方法会放弃这个对象的监视器。
wait需要等待唤醒,而sleep睡眠一定时间之后自动苏醒。
继承Thread类 重写run方法
启动线程是调用start方法,这样会创建一个新的线程,并执行线程的任务。
如果直接调用run方法,这样会让当前线程执行run方法中的业务逻辑。
public class MiTest {
public static void main(String[] args) {
MyJob t1 = new MyJob();
t1.start();
for (int i = 0; i < 100; i++) {
System.out.println("main:" + i);
}
}
}
class MyJob extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyJob:" + i);
}
}
}
实现Runnable接口 重写run方法
public class MiTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
t1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main:" + i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("MyRunnable:" + i);
}
}
}
sleep有两个方法重载:
第一个就是native修饰的,让线程转为等待状态的效果
第二个是可以传入毫秒和一个纳秒的方法(如果纳秒值大于等于0.5毫秒,就给休眠的毫秒值+1。如果传入的毫秒值是0,纳秒值不为0,就休眠1毫秒)
sleep会抛出一个InterruptedException
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(System.currentTimeMillis());
}
缓存一致性(MESI)
用于保证多个 CPU cache 之间缓存共享数据的一致
M-modified被修改
该缓存行只被缓存在该 CPU 的缓存中,并且是被修改过的,与主存中数据是不一致的,需在未来某个时间点写回主存,该时间是允许在其他CPU 读取主存中相应的内存之前,当这里的值被写入主存之后,该缓存行状态变为 E
E-exclusive独享
缓存行只被缓存在该 CPU 的缓存中,未被修改过,与主存中数据一致
可在任何时刻当被其他 CPU读取该内存时变成 S 态,被修改时变为 M态
S-shared共享
该缓存行可被多个 CPU 缓存,与主存中数据一致
I-invalid无效
线程安全性
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的
synchronized 修正计数类方法
修饰类:括号括起来的部分,作用于所有对象
子类继承父类的被 synchronized 修饰方法时,是没有 synchronized 修饰的!!!
Lock: 依赖特殊的 CPU 指令,代码实现
![QQ截图20231220144119.png](https://static.golangjob.cn/231220/0472ef9629b9f7e716623851a107c083.png)
有疑问加站长微信联系(非本文作者)
![](https://static.golangjob.cn/static/img/footer.png?imageView2/2/w/280)