一、多线程数据不同步产生的原因

java 同步代码块 java同步机制方法_多线程


由于每个线程工作时,都是把主内存(堆内存)里面的值复制一份到到工作内存中使用,而一般来说,线程开启后,该复制行为只会发生一次,故当其他线程在线程结束后,如果做了更改主内存的行为,其他线程无法得到该更新的值

二、线程数据同步的思路

要使线程数据同步,即要使 :
 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等基本类型一样, 内存被分配在栈上了,即所谓的栈上分配,,,故,不会同步堆内存属性

java 同步代码块 java同步机制方法_多线程_02


代码示例:

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;
    }
}