多线程同步异步,以及volatile关键字的作用
#多线程同步异步
多线程同步异步一直以来都是很热门讨论的话题,根据自己这段时间的学习总结下线程的同步异步问题
所谓同步直白了说就是让代码按照你写的顺序一行一行的执行,上一段代码执行完了返回了相对应的结果,根据结果再执行下一段代码,这就是所谓的同步。然而现实总是会出现一些不按照你的要求来的代码,上一段代码还没执行完,下一段代码就开始执行了,并没有按照你的顺序来执行,这时最产生了异步问题。最简单的应用,前端请求接口的时候,一个页面刚打开的时候会同时执行多个接口,接口返回的顺序可能并不是你所写的代码的顺序,如果想要按照你写的代码的顺序来执行,就需要用到同步了,今天主要说的是java后台多线程之间的同步异步以及关键字volatile的作用和用法。
先贴一段代码
```java
package episode.test15;
public class MyThread extends Thread{
public static int count;
private static void addCount() {
for(int i=0;i<100;i++) {
count++;
}
System.out.println("count="+count);
}
public void run() {
addCount();
}
}
package episode.test15;
public class Run {
public static void main(String[] args) {
MyThread[] myThreadArray = new MyThread[100];
for(int i=0;i<100;i++) {
myThreadArray[i] = new MyThread();
}
for(int i=0;i<100;i++) {
myThreadArray[i].start();
}
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191027172200709.png)很明显看出来线程不是同步的,想要线程同步得再方法前加上synchronized关键字。不过synchronized关键字让方法同步了,却也是每次都得其他线程等待当前线程执行完成,才可以再执行下一个线程。
看下volatile关键字,先贴一段代码
```java
package episode.test14;
public class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setisContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while(isContinuePrint) {
System.out.println("run printStringMethod thread="+Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package episode.test14;
public class Run {
public static void main(String[] args) {
PrintString printStringService = new PrintString();
printStringService.printStringMethod();
System.out.println("我要停止它! stopThread="+Thread.currentThread().getName());
printStringService.setisContinuePrint(false);
}
}
运行结果如下图所以:
可以看到线程进入了死循环,“我要停止他”这段文字并未打印出来,这时候我们可以用多线程技术来解决这个问题代码如下
package episode.test14;
public class PrintString2 implements Runnable{
private boolean isContentPrint = true;
public boolean isContentPrint() {
return isContentPrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContentPrint = isContentPrint;
}
public void printStringMethod() {
try {
while(isContentPrint == true) {
System.out.println("run printStringMethod threadName="+Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
printStringMethod();
}
}
package episode.test14;
public class Run2 {
public static void main(String[] args) {
PrintString2 printStringService = new PrintString2();
new Thread(printStringService).start();
System.out.println("我要停止它!stopThread="+Thread.currentThread().getName());
printStringService.setContinuePrint(false);
}
}
运行结果如图所示:
虽然system中的内容打印了出来,但是线程依然进入了死循环。我们对程序稍加改动,在变量前加上volatile看看结果如下:
贴上代码如下:
package episode.test14;
public class RunThread extends Thread{
volatile boolean isRunning = true;
public boolean AisRunning() {
return isRunning;
}
public void setRuning(boolean isRunning) {
this.isRunning = isRunning;
}
public void run() {
System.out.println("线程启动进入run了");
while(isRunning) {
}
System.out.println("线程被停止了!");
}
}
结果如下:
这是因为再未使用volatile关键字之前,变量private boolean isRunning=true存在于公共堆栈和私有堆栈中,每次改变变量值只改变了公共堆栈的值,而线程私有堆栈中的值却并未改变,所以线程一直处于死循环状态。加上volatile关键字之后,强制从公共堆栈中取值,因此线程可以跳出死循环。虽然volatile关键字增加了实力变量在多个线程间的可见想,但是volatile关键字并不是原子性,也就是说它并不能保证变量是同步的,是线程安全的。还是引用最开头的代码,在变量前加上volatile进行实验,结果如下与未加volatile关键字是一样的,因此若要实现同步线程安全,还是得在方法前加上synchronized关键字。综上所述,volatile关键字只是强制将变量从共有堆栈中取出来,并不能保证变量的原子性,想要保证变量的原子性还是要用synchronized关键字