标签:竞争条件 ber 进制 pen pool 属性 int a+b kde
Java并发相关知识集锦
class文件包含JAVA程序执行的字节码;数据严格按照格式(虚拟机要求的一种规范)紧凑排列在class文件中的二进制流,中间无任何分隔符;文件开头有一个0xcafebabe(16进制)特殊的一个标志。
这类文件专门给JVM读里面的内容,因此具有很复杂的格式,程序员阅读可以进行工具查看。
Demo1.class的内容如下:
public class Demo1 {
public static void main(String[] args){
int x = 500;
int y = 100;
int a = x/y;
int b = 50;
System.out.println(a+b);//55
}
}
//在java文件的根目录打开cmd命令窗口,再输入下面的命令。
//查看Java版本
java -version
//在Demo1类文件的根目录进行编译
javac Demo1.class
//javap命令查看内容
javap -v Demo1.class>Demo1.txt
>
作用是将生成的内容,保存到Demo1.txt中。
下面我们分析下Demo1.txt中的内容吧!
public class cn.suvue.discipline.practice.demo.Demo1
minor version: 0 //次版本号
major version: 52 //主版本号
flags: ACC_PUBLIC, ACC_SUPER //访问标志
Constant pool:
#1 = Methodref #5.#14 // java/lang/Object."<init>":()V
#2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V
#4 = Class #19 // cn/suvue/discipline/practice/demo/Demo1
#5 = Class #20 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 SourceFile
#13 = Utf8 Demo1.java
#14 = NameAndType #6:#7 // "<init>":()V
#15 = Class #21 // java/lang/System
#16 = NameAndType #22:#23 // out:Ljava/io/PrintStream;
#17 = Class #24 // java/io/PrintStream
#18 = NameAndType #25:#26 // println:(I)V
#19 = Utf8 cn/suvue/discipline/practice/demo/Demo1
#20 = Utf8 java/lang/Object
#21 = Utf8 java/lang/System
#22 = Utf8 out
#23 = Utf8 Ljava/io/PrintStream;
#24 = Utf8 java/io/PrintStream
#25 = Utf8 println
#26 = Utf8 (I)V
public cn.suvue.discipline.practice.demo.Demo1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC //描述方法的访问控制
Code:
//stack:方法对应栈帧中操作数栈的深度
//locals:本地变量数量
//size:参数数量
stack=3, locals=5, args_size=1
//下面是JVM执行引擎去执行这些源码编译过的指令码。
//javap命令翻译出来的是操作符,class文件内存储的是指令码。
//前面的数字,是偏移量(字节),jvm根据这个区分不同的指令。详情可百度JVM指令码表。
0: sipush 500
3: istore_1
4: bipush 100
6: istore_2
7: iload_1
8: iload_2
9: idiv
10: istore_3
11: bipush 50
13: istore 4
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_3
19: iload 4
21: iadd
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
25: return
Stop:终止线程,并且清除监视器的信息,但是可能导致线程安全问题,JDK不建议用。
Destroy:JDK未实现该方法。
来看下代码演示吧
package cn.suvue.discipline.practice.demo;
/**
* 演示线程stop方法的错误用法
*
* 输出结果为i=1 j=0 没能够保证数据一致性
* @author suvue
* @date 2020/2/6
*/
class StopThread extends Thread{
private int i,j;
@Override
public void run() {
synchronized (this){
++i;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
++j;
}
}
public void print(){
System.out.println("i="+i+" j="+j);
}
}
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(2000);
thread.stop();
while (thread.isAlive()){
}
thread.print();
}
}
输出结果为i=1 j=0 没能够保证数据一致性。
如果目标线程在调用Object class 的wait()、wait(long) 或wait(long,int)方法、join()、join(long,int)或sleep(long,int)方法时被阻塞,那么Interrupt会生效,该线程的中断状态将被清除,抛出InterruptedException异常。
如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值,达到终止线程的目的。
如果以上条件都不满足,则会设置此线程的中断状态。
对于Demo3中的示例,stop改成interrupt后,最终输出为“i=1 j=1” , 数据一致。
总结一下:
代码逻辑中,增加一个判断,用来控制线程执行的中止。
public class Demo4 extends Thread{
public volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
while (flag) { // 判断是否运行
System.out.println("运行中");
Thread.sleep(1000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 3秒之后,将状态标志改为False,代表不继续运行
Thread.sleep(3000L);
flag = false;
System.out.println("程序运行结束");
}
}
为了提高程序运行的性能,现代CPU在很多方面对程序进行了优化。例如:CPU高速缓存,尽可能地避免处理器访问主内存的时间开销,处理器大多会利用缓存以提高性能。
多CPU读取同样的数据进行缓存,进行不同运算之后,最终写入主内存以哪个CPU为准?在这种高速缓存回写的场景下,定义了缓存一致性协议供多数CPU厂商对它进行实现。
MESI协议,它规定每条缓存有个状态位,同时定义了下面四个状态:
总结一下
处理器提供了两个内存屏障指令(Memory Barrier)用于解决上述两个问题:
要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行的结果等等。涉及到线程之间相互通信,分为下面四类:
1)文件共享
2)网络共享
3)共享变量
4)jdk提供的线程协调API细分为:suspend/resume、wait/notify、park/unpark
JDK中对于需要多线程协作完成某一任务的场景,提供了对应的API支持。
多线程协作的典型场景:生产者 - 消费者模型。(线程阻塞、线程唤醒)
示例:线程1去买包子,没有包子,则不再执行。线程-2生产出包子,通知线程-1继续执行。
作用:调用suspend挂起目标线程,通过resume可以恢复线程执行。
public static Object baozidian = null;
public void suspendResumeTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
System.out.println("进入等待");
Thread.currentThread().suspend();
}
System.out.println("买到包子,准备回家");
});
consumeThread.start();
//3秒钟之后 生产一个包子
Thread.sleep(3000);
baozidian=new Object();
consumeThread.resume();
System.out.println("通知消费者");
}
死锁示例
public static Object baozidian = null;
/**
* 死锁的suspend/resume。 suspend并不会像wait一样释放锁,故此容易写出死锁代码
*/
public void suspendResumeDeadLockTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
System.out.println("进入等待");
synchronized (this) {
Thread.currentThread().suspend();
}
}
System.out.println("买到包子,回家");
});
consumeThread.start();
//3秒之后生产包子
Thread.sleep(3000);
baozidian = new Object();
//争取到锁之后,再恢复consumeThread
synchronized (this) {
consumeThread.resume();
}
System.out.println("通知消费者");
}
public static Object baozidian = null;
/**
* 导致程序永久挂起的suspend/resume
*/
public void suspendResumeDeadLockTest2() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
System.out.println("进入等待");
try {//这里模拟一点延迟
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.currentThread().suspend();
}
System.out.println("买到包子,回家");
});
consumeThread.start();
//等待3秒,生产包子
Thread.sleep(3000);
baozidian = new Object();
consumeThread.resume();
System.out.println("通知消费者");
}
public static Object baozidian = null;
/**
* 正常的waitNotify
*/
public void waitNotifyTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
synchronized (this) {
try {
System.out.println("进入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("买到包子,回家");
});
consumeThread.start();
Thread.sleep(3000);
baozidian = new Object();
synchronized (this) {
this.notifyAll();
System.out.println("通知消费者");
}
}
public static Object baozidian = null;
/**
* 会导致程序永久等待的wait/notify
*/
public void waitNotifyDeadLockTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
try {//模拟延迟
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
try {
System.out.println("进入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("买到包子,回家");
}
});
consumeThread.start();
//3秒之后,生产包子
Thread.sleep(3000);
baozidian = new Object();
synchronized (this){
this.notifyAll();
}
System.out.println("通知消费者");
}
public static Object baozidian = null;
/**
* 正常的park/unpark
*/
public void parkUnParkTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
try { //模拟延迟,来调换park/unpark的调用顺序
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("进入等待");
LockSupport.park();
}
System.out.println("买到包子,回家");
});
consumeThread.start();
//3秒之后生产包子
Thread.sleep(3000);
baozidian = new Object();
LockSupport.unpark(consumeThread);
System.out.println("通知消费者");
}
public static Object baozidian = null;
/**
* 死锁的park/unpark
*/
public void parkUnParkDeadLockTest() throws InterruptedException {
Thread consumeThread = new Thread(() -> {
while (baozidian == null) {
synchronized (this){
System.out.println("进入等待");
//注意这里挂起之后不会释放对象锁
LockSupport.park();
}
}
System.out.println("买到包子,回家");
});
consumeThread.start();
//3秒之后生产包子
Thread.sleep(3000);
baozidian = new Object();
synchronized (this){
LockSupport.unpark(consumeThread);
}
System.out.println("通知消费者");
}
while (条件判断) {//这里用while代替if判断
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//...后续操作
}
}
API | 线程挂起后是否会自动释放对象锁 | 调用次序是否影响程序执行 |
---|---|---|
suspend/resume | 否 | 是 |
wait/notify | 是 | 是 |
park/unpark | 否 | 否 |
多线程访问共享可变数据时,涉及到线程间数据同步的问题。并不是所有时候,都会用到共享数据,所以线程封闭的概念就提出来了。
private static ThreadLocal<String> values = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
values.set("主线程存放的数据");
System.out.println("线程-1启动之前主线程:"+values.get());
new Thread(() -> {
values.set("线程-1存放的数据");
System.out.println("线程-1:"+values.get());
}).start();
Thread.sleep(3000);
System.out.println("线程-1启动之后主线程:"+values.get());
}
类型 | 名称 | 描述 |
---|---|---|
接口 | Executor | 最上层的接口,定义了执行任务的方法execute |
接口 | ExecuService | 继承了Executor接口,拓展了Callable、Future、关闭方法 |
接口 | ScheduledExecutorService | 继承了ExecutorService,增加了定时任务相关的方法 |
实现类 | ThreadPoolExecutor | 基础、标准的线程池实现 |
实现类 | ScheduledThreadPoolExecutor | 继承了ThreadPoolExecutor,实现了ScheduledExecutorService中相关定时任务的方法 |
ScheduledExecutorService类
我们可以自己实例化线程池,也可以用Executors创建线程池的工具类,常用方法如下:
标签:竞争条件 ber 进制 pen pool 属性 int a+b kde
原文地址:https://www.cnblogs.com/suvue/p/12332294.html