线程范围内的共享变量是指对同一个变量,几个线程同时对它进行写和读操作,而同一个线程读到的数据就是它自己写进去的数据。
1、未实现变量共享的例子:

import java.util.Random;

public class NotShareVarThread {
    private static int data = 0;
    public static void main(String[] args){

        for(int i = 0; i < 2; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {

                        data = new Random().nextInt();
                        System.out.println(Thread.currentThread().getName()+"将data修改为"+ data);
                        new A().get();
                        new B().get();

                }
            }).start();
        }

    }
    //类A
    static class A{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_A类中的数据值"+ data);
        }
    }
    //类B
    static class B{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_B类中的数据值"+ data);
        }
    }
}

从结果可以看出,线程1将线程0修改后的data值覆盖了,并且输出的结果是线程1中修改的值。

运行结果

"C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\lib\idea_rt.jar=15962:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\java\java_Basics\out\production\java_Basics;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\junit-4.12.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\bcprov-jdk15on-1.64.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\hamcrest-core-1.3.jar" cn.itcast.ThreadStudyday01.NotShareVarThread
Thread-0将data修改为2042746135
Thread-1将data修改为-393110833
Thread-1_A类中的数据值-393110833
Thread-0_A类中的数据值-393110833
Thread-1_B类中的数据值-393110833
Thread-0_B类中的数据值-393110833

Process finished with exit code 0

那么该如何来解决这个问题呢?
2、线程范围内变量共享的实现方式:
一、通过线程的同步互斥实现

使用同步代码块,将变量修改以及访问的代码加锁,线程0访问结束才允许线程1来修改data的值,代码如下:

package cn.itcast.ThreadStudyday01;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class NotShareVarThread {
    private static int data = 0;
    public static void main(String[] args){

        for(int i = 0; i < 2; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                        synchronized (NotShareVarThread.class){
                            data = new Random().nextInt();
                            System.out.println(Thread.currentThread().getName()+"将data修改为"+ data);
                            new A().get();
                            new B().get();
                        }
                }
            }).start();
        }

    }
    //类A
    static class A{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_A类中的数据值"+ data);
        }
    }
    //类B
    static class B{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_B类中的数据值"+ data);
        }
    }
}

运行结果:

"C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\lib\idea_rt.jar=16065:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\java\java_Basics\out\production\java_Basics;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\junit-4.12.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\bcprov-jdk15on-1.64.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\hamcrest-core-1.3.jar" cn.itcast.ThreadStudyday01.NotShareVarThread
Thread-0将data修改为-1215611266
Thread-0_A类中的数据值-1215611266
Thread-0_B类中的数据值-1215611266
Thread-1将data修改为-282471283
Thread-1_A类中的数据值-282471283
Thread-1_B类中的数据值-282471283

Process finished with exit code 0

二、通过Map的key/value键值对,将线程与其对应的数据绑定

通过Map的特性,将key为当前线程,value为对应的值。

测试代码:

package cn.itcast.ThreadStudyday01;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class NotShareVarThread {
    private static Map<Thread,Integer>  data = new HashMap<>();
    public static void main(String[] args){

        for(int i = 0; i < 2; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                        synchronized (NotShareVarThread.class){
                            int value = new Random().nextInt();
                            data.put(Thread.currentThread(),value);
                            System.out.println(Thread.currentThread().getName()+"将data修改为"+ value);
                            new A().get();
                            new B().get();
                        }
                }
            }).start();
        }

    }
    //类A
    static class A{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_A类中的数据值"+ data.get(Thread.currentThread()));
        }
    }
    //类B
    static class B{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_B类中的数据值"+ data.get(Thread.currentThread()));
        }
    }
}

运行结果:

"C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\lib\idea_rt.jar=16167:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\java\java_Basics\out\production\java_Basics;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\junit-4.12.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\bcprov-jdk15on-1.64.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\hamcrest-core-1.3.jar" cn.itcast.ThreadStudyday01.NotShareVarThread
Thread-0将data修改为1995825136
Thread-0_A类中的数据值1995825136
Thread-0_B类中的数据值1995825136
Thread-1将data修改为-1933982967
Thread-1_A类中的数据值-1933982967
Thread-1_B类中的数据值-1933982967

Process finished with exit code 0

三、通过ThreadLocal实现:
ThreaLocal类在维护变量时,实际使用了当前线程(Thread)中的一个叫做 ThreadLocalMap的独立副本(Thread的成员变量),每个线程可以独立的修改属于自己的副本而不会相互影响,从而实现了线程之间的隔离,避免了线程访问实例变量发生冲突的问题。

示例代码:

import java.util.Random;

public class NotShareVarThread {
    private static ThreadLocal<Integer> data = new ThreadLocal<>();
    public static void main(String[] args){

        for(int i = 0; i < 2; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {

                        int value = new Random().nextInt();
                        data.set(value);
                        System.out.println(Thread.currentThread().getName()+"将data修改为"+ value);
                        new A().get();
                        new B().get();

                }
            }).start();
        }

    }
    //类A
    static class A{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_A类中的数据值"+ data.get());
        }
    }
    //类B
    static class B{
        public void get(){
            System.out.println(Thread.currentThread().getName()+"_B类中的数据值"+ data.get());
        }
    }
}

运行结果:

"C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\lib\idea_rt.jar=15743:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\java\java_Basics\out\production\java_Basics;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\junit-4.12.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\bcprov-jdk15on-1.64.jar;C:\IntelliJ IDEA Community Edition 2019.2.1\lib\hamcrest-core-1.3.jar" cn.itcast.ThreadStudyday01.NotShareVarThread
Thread-1将data修改为1230064644
Thread-0将data修改为-2874308
Thread-0_A类中的数据值-2874308
Thread-1_A类中的数据值1230064644
Thread-1_B类中的数据值1230064644
Thread-0_B类中的数据值-2874308

Process finished with exit code 0

3、小结
1) ThreadLocal是一个Java类,通过对当前线程中局部变量的操作来解决不同线程的访问变量的冲突问题。所以,ThreadLocal提供了线程安全的共享机制,每个线程都拥有各自的副本(ThreadLocalMap实例)
2) Java中的synchronized是一个关键字,它依靠JVM的锁机制来实现临界区的函数或者变量在访问时的原子性。在同步机制中,通过对象的锁机制来保证同一时刻只有一个线程访问变量。
同步机制采取了以“以时间换空间”的方式,提供一个变量,让线程排队访问。而ThreadLocal则是采用了以“空间换取时间”的方式,为每一个线程都提供一份变量副本,从而实现同时访问而互不影响。