一、多线程数据不同步产生的原因
由于每个线程工作时,都是把主内存(堆内存)里面的值复制一份到到工作内存中使用,而一般来说,线程开启后,该复制行为只会发生一次,故当其他线程在线程结束后,如果做了更改主内存的行为,其他线程无法得到该更新的值
二、线程数据同步的思路
要使线程数据同步,即要使 :
1、数据更改方:有将操作后数据同步到主存上的操作,比如结束线程,或同步锁等
2、数据获取方:有在线程运行时,再次读取主存上的数据的的操作,同步锁、调用方法,进而沟通堆内存等
三、线程同步数据的具体实现
思路:
1、线程同步锁 :
线程同步锁要求每次代码块运行前后,都必须与主内存变量同步,故使用同一把锁的代码块间的数据可以借由堆内存实现数据共享
代码示例:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DemoApplication {
static AddClass i = new AddClass();
public static void main(String[] args) {
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
synchronized (myThread[k]){
try {
//线程0开启后,如果回到主线程,则主线程等待,因为JAVA代码是顺序执行的
myThread[k].wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public synchronized void start() {
super.start();
notify();
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DemoApplication {
static AddClass i = new AddClass();
public static void main(String[] args) {
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
synchronized (myThread[k]){
try {
//线程0开启后,如果回到主线程,则主线程等待,因为JAVA代码是顺序执行的
myThread[k].wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public synchronized void start() {
super.start();
notify();
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}
2、调用方法(不太稳妥)
思路:调用方法时,所调用方法会从堆中获取数据,借此实现线程同步,偶尔会有bug,原因不明,慎用
原理是调用方法时,所调用方法会请求最新的堆内存
获取对象的公有属性也可以算作独特的调用方法,注意,不能直接使用Integer的包装类作为线程内的共享对象,Integer等包装类可能与int等基本类型一样, 内存被分配在栈上了,即所谓的栈上分配,,,故,不会同步堆内存属性
代码示例:
package com.example.demo;
public class DemoApplication {
static AddClass i = new AddClass();
public static void main(String[] args) throws Exception{
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
Thread.sleep(1000);
System.out.println("main中输出"+i.getI());
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}
package com.example.demo;
public class DemoApplication {
static AddClass i = new AddClass();
public static void main(String[] args) throws Exception{
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
Thread.sleep(1000);
System.out.println("main中输出"+i.getI());
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}
3、volatile(不太稳妥,据说多线程会出现问题)
要求更新时去主存中存储数据
使用包装类时,如Integer,后两种方法皆不可行,原因未知
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DemoApplication {
static volatile AddClass i = new AddClass();
public static void main(String[] args) {
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public synchronized void start() {
super.start();
//notify();
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DemoApplication {
static volatile AddClass i = new AddClass();
public static void main(String[] args) {
MyThread [] myThread = new MyThread[7];
myThread[0] = new MyThread(i);
for (int k = 0; k < 6; ) {
myThread[k].start();
//下一个线程开启
myThread[++k] = new MyThread(i);
}
}
}
class MyThread extends Thread{
AddClass i ;
public MyThread(AddClass i) {
this.i = i;
}
@Override
public synchronized void start() {
super.start();
//notify();
}
@Override
public void run() {
System.out.println("开始运行");
int k = i.getI();
for (; i.getI() < k + 5; i.Add()) {
System.out.println(i.getI() + " ");
}
System.out.println("正常运行结束!");
}
}
class AddClass{
Integer i = 0;
void Add(){
i=i+5;
}
@Override
public String toString() {
return "AddClass{" +
"i=" + i +
'}';
}
public Integer getI() {
return i;
}
}