在学习线程的时候,确定会遇到线程同步问题java
多个并发线程之间按照某种机制协调前后次序执行,当有一个线程在对内存进行操做时,其余线程都不能够对这个内存地址进行操做,直到该线程完成操做编程
根据上面的定义咱们能够知道,线程同步问题主要是多线程并发状况下如何互斥访问共享资源;在多个线程对同一变量进行读写操做时,若是没有原子性,就可能产生脏数据。实现线程同步有不少的方式,好比同步方法,锁,阻塞队列等。bash
那么java中的线程是如何访问资源的呢,在学习了一段时间的jvm后,对于线程访问共享资源有了更深的理解多线程
Java虚拟机在运行Java程序时,会把它所管理的内存区域划分为若干个不一样的区域,每一个区域都有各自的用途;下图是经典的JVM内存布局 并发
程序计数器 (PC): 当前线程所执行的字节码的行号指示器,用于线程切换后能恢复到正确的执行位置jvm
Java虚拟机栈: 每一个线程都有一个本身的Java栈, 描述了Java方法运行时的内存模型:每一个方法在执行时都会建立一个栈帧用于储存局部变量表、操做数栈、动态连接、方法出口等信息;每一个方法的从开始调用到执行完成的过程,就是栈帧从入栈到出栈的过程ide
本地方法栈:与JAVA栈相似,区别是使用的对象不同,本地方法栈是给Native方法使用的,JAVA虚拟机栈是给JAVA方式使用的函数
在没有任何同步机制的状况下,多个线程共享一块内存区域,对其操做;布局
不使用共享内存,每一个线程内存空间相互独立;学习
多线程共享一块内存区域,可是对这块共享区域加锁访问;
对静态变量sum进行操做,静态变量保存在线程共享的内存区域中,多个线程能够同时访问
package com.thread;
/**
* @Author: ranjun
* @Date: 2019/7/22 14:13
*/
public class AddTest {
private static int sum = 0;
/**
* 对静态变量sum进行累加操做
* @param n
* @return
*/
public static int sum(int n){
sum = 0;
for(int i = 0; i <= n; i++){
sum += i;
//让出cpu资源,让其余线程执行;
//能够将下面这段注释掉,若是累加次数足够少时,仍然会获得正确结果(当前线程能够在当前时间片中完成累加操做,并返回值)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sum;
}
}
复制代码
写个main函数,定义四个线程,每一个线程都调用上面的静态方法,观察运行结果,基本都是错误的:
package com.thread;
/**
* @Author: ranjun
* @Date: 2019/7/22 14:17
*/
public class MainTest {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName() +":" +AddTest.sum(100));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
复制代码
部分运行结果,都是错的
Thread-3:20153
Thread-1:19953
Thread-2:19953
Thread-0:20153
Thread-1:18510
Thread-3:18510
复制代码
缘由:多个线程同时对静态全局变量s进行操做致使;
ps:这里的例子是静态全局变量s,其实有不少种状况会引发结果异常问题,如在main方法中new出了一个对象,new出来的对象是存放在堆中的,多个线程共享,此时若是多线程同时操做该对象的话,也是有可能产生错误结果;
修改静态sum方法,使用局部变量sum,以下:
public static int sum(int n){
int sum = 0;
for(int i = 0; i <= n; i++){
sum += i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sum;
}
复制代码
运行程序,结果正确:
Thread-3:5050
Thread-1:5050
Thread-2:5050
Thread-0:5050
Thread-0:5050
Thread-1:5050
Thread-2:5050
Thread-3:5050
复制代码
public synchronized static int sum(int n){
sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sum;
}
复制代码
运行程序,结果正确:
Thread-1:5050
Thread-3:5050
Thread-2:5050
Thread-0:5050
Thread-2:5050
复制代码
从上面的例子能够看到,多线程对共享资源的操做结果是难以控制的;因此在多线程编程过程当中,咱们要知道哪些资源是线程共享的,哪些是线程本地私有的,而后合理的对共享资源进行协调。