实例变量的不共享
自定义线程类中的实例对于其他线程来说可以有共享与不共享之分,这是多线程的核心知识点之一。
不共享情景如下所示
创建代码 test5.java
下面通过一个示例看下数据的不共享情况,创建 test5.java 类,并输入如下代码:
class MyThread extends Thread{private int count = 5;public MyThread(String name){super();this.setName(name);}@Overridepublic void run(){while(count > 0){count --;System.out.println(Thread.currentThread().getName() + ":.." + count);}}}public class test5{public static void main(String[] args){Thread myThread_A = new MyThread("A");Thread myThread_B = new MyThread("B");Thread myThread_C = new MyThread("C");Thread myThread_D = new MyThread("D");Thread myThread_E = new MyThread("E");myThread_A.start();myThread_B.start();myThread_C.start();myThread_D.start();myThread_E.start();}}
class MyThread extends Thread{ private int count = 5; public MyThread(String name){ super(); this.setName(name); } @Override public void run(){ while(count > 0){ count —; System.out.println(Thread.currentThread().getName() + “:..” + count); } } } public class test5{ public static void main(String[] args){ Thread myThread_A = new MyThread(“A”); Thread myThread_B = new MyThread(“B”); Thread myThread_C = new MyThread(“C”); Thread myThread_D = new MyThread(“D”); Thread myThread_E = new MyThread(“E”); myThread_A.start(); myThread_B.start(); myThread_C.start(); myThread_D.start(); myThread_E.start(); } }
test5 运行结果
运行 test5 实例数据不共享情况的测试之后,输出结果如下所示:
可以看到,一共创建了 5 个线程,每个线程都有各自的 count 变量,自己减少自己的 count 值,这样的情况便是实例变量不共享,此示例不存在多个线程访问同一个变量的情况。
实例变量的共享
共享情景如下所示
创建代码 test6.java
下面通过一个示例看下数据的共享情况,创建 test6.java 类,并输入如下代码:
class MyThread extends Thread{private int count = 5;@Overridepublic void run(){count --;System.out.println(Thread.currentThread().getName() + ":.." + count);}}public class test6{public static void main(String[] args){MyThread myThread = new MyThread();Thread thread_A = new Thread(myThread,"A");Thread thread_B = new Thread(myThread,"B");Thread thread_C = new Thread(myThread,"C");Thread thread_D = new Thread(myThread,"D");Thread thread_E = new Thread(myThread,"E");thread_A.start();thread_B.start();thread_C.start();thread_D.start();thread_E.start();}}
class MyThread extends Thread{ private int count = 5; @Override public void run(){ count —; System.out.println(Thread.currentThread().getName() + “:..” + count); } } public class test6{ public static void main(String[] args){ MyThread myThread = new MyThread(); Thread thread_A = new Thread(myThread,”A”); Thread thread_B = new Thread(myThread,”B”); Thread thread_C = new Thread(myThread,”C”); Thread thread_D = new Thread(myThread,”D”); Thread thread_E = new Thread(myThread,”E”); thread_A.start(); thread_B.start(); thread_C.start(); thread_D.start(); thread_E.start(); } }
test6 运行结果
第一次运行 test6 测试结果如下所示

如图所示我们所创建的 5 个线程,分别对 count 数值进行了 — 操作,此时这 5 个线程即为共享实例变量。
第二次运行 test6 测试结果如下所示

因为多线程之间是不断争夺 CPU 资源的,所以会导致上面这种不安全的情况产生。每一次多线程的测试结果都不会相同,但是此时由于我们新建的线程数量太少,效果还不太明显,具体可看下面 test7 的例子。
感受实例变量共享的安全问题
从 test6 的测试结果我们可以初步看出,多线程在并发执行的时候,可能会重复删除数据、或未删除数据,等等情况产生,本小节将对多线程实例变量共享的安全问题进行放大,让读者更清晰的感受。
创建代码 test7.java
test7 中将 count 改为 50,并且用循环的方式连续创建了 30 个线程。
class MyThread extends Thread{private int count = 50;@Overridepublic void run(){count --;System.out.println(Thread.currentThread().getName() + " :.." + count);}}public class test7{public static void main(String[] args){MyThread myThread = new MyThread();for(int i = 0; i < 30; i++){Thread thread = new Thread(myThread, "i+" + i);thread.start();}}}
class MyThread extends Thread{ private int count = 50; @Override public void run(){ count —; System.out.println(Thread.currentThread().getName() + “ :..” + count); } } public class test7{ public static void main(String[] args){ MyThread myThread = new MyThread(); for(int i = 0; i < 30; i++){ Thread thread = new Thread(myThread, “i+” + i); thread.start(); } } }
test7 运行结果
第一次运行 test7 测试结果如下所示
第二次运行 test7 测试结果如下所示

test7 第二次运行的时候最后结果是 21.
36 35 34 33 34 32 31 这几个数值出现的时候,线程产生了混乱,已经没办法正确 -1 了。
下一步
初次解决实例变量共享的安全问题
我们将利用 synchronized 关键字来初次解决实例变量共享的安全问题。
初步认识 synchronized 关键字
在 test8 的测试之中,通过在 run 方法前加入 synchronized 关键字,使多个线程在执行 run 方法时,以排队的方式进行处理。
当一个线程想要执行同步方法里面的代码之前,会先判断 run 方法有没有被上锁,如果被上锁,说明有其他线程正在调用 run 方法,必须等其它线程对 run 方法调用结束后才可以执行 run 方法。这样也就实现了排队调用 run 方法的目的,也就达到了解决实例变量共享的安全问题的目的。
synchronized 可以在任意对象及方法上加锁,而加锁的这段代码被称之为“互斥区”或“临界区”。
创建代码 test8.java
class MyThread extends Thread{private int count = 50;@Overridesynchronized public void run(){count --;System.out.println(Thread.currentThread().getName() + " :.." + count);}}public class test8{public static void main(String[] args){MyThread myThread = new MyThread();for(int i = 0; i < 30; i++){Thread thread = new Thread(myThread, "i+" + i);thread.start();}}}
class MyThread extends Thread{ private int count = 50; @Override synchronized public void run(){ count —; System.out.println(Thread.currentThread().getName() + “ :..” + count); } } public class test8{ public static void main(String[] args){ MyThread myThread = new MyThread(); for(int i = 0; i < 30; i++){ Thread thread = new Thread(myThread, “i+” + i); thread.start(); } } }
test7 运行结果


