局部变量的引用
先看一个成员变量的例子
class ThreadUnsafe {
ArrayList list = new ArrayList();
/**
* 两个线程同时执行,其中一个线程还没有往list中添加数据,
* 另一个线程已经从list中取出了数据
* 就会产生线程安全问题
*
* @param loopNumber
*/
public void method1(int loopNumber) {
for (int i = 0; i {
test.method1(LOOP_NUMBER);
}, "Thread" + i).start();
}
}
}
其中一种情况是,如果线程2 还未 add,线程1 remove 就会报错:
Exception in thread "Thread1" Exception in thread "Thread0" java.lang.ArrayIndexOutOfBoundsException: -2
at java.util.ArrayList.remove(ArrayList.java:507)
at cn.itcast.test.ThreadUnsafe.method3(ThreadUnsafe.java:29)
at cn.itcast.test.ThreadUnsafe.method1(ThreadUnsafe.java:19)
at cn.itcast.test.ThreadUnsafe.lambda$main$0(ThreadUnsafe.java:40)
at java.lang.Thread.run(Thread.java:748)
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.remove(ArrayList.java:507)
at cn.itcast.test.ThreadUnsafe.method3(ThreadUnsafe.java:29)
at cn.itcast.test.ThreadUnsafe.method1(ThreadUnsafe.java:19)
at cn.itcast.test.ThreadUnsafe.lambda$main$0(ThreadUnsafe.java:40)
at j服务器托管网ava.lang.Thread.run(Thread.java:748)
分析:
无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量
method3 与 method2 分析相同
将 list 修改为局部变量
class ThreadUnsafe {
public final void method1(int loopNumber) {
ArrayList list = new ArrayList();
for (int i = 0; i list) {
list.add("1");
}
private void method3(ArrayList list) {
list.remove(0);
}
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i {
test.method1(LOOP_NUMBER);
}, "Thread" + i).start();
}
}
}
那么就不会有上述问题了
分析:
list 是局部变量,每个线程调用时会创建其不同实例,没有共享
而 method2 的参数是从 method1 中传递过来的,与 method1 中引用同一个对象
method3 的参数分析与 method2 相同
方法访问修饰符带来的思考,如果把 method2 和 method3 的方法修改为 public 会不会服务器托管网代理线程安全问题?
- 情况1:有其它线程调用 method2 和 method3
- 情况2:在 情况1 的基础上,为 ThreadSafe 类添加子类,子类覆盖 method2 或 method3 方法,即
public class TestThreadSafe {
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public static void main(String[] args) {
ThreadSafeSubClass test = new ThreadSafeSubClass();
for (int i = 0; i {
test.method1(LOOP_NUMBER);
}, "Thread" + (i+1)).start();
}
}
}
class ThreadUnsafe {
ArrayList list = new ArrayList();
public void method1(int loopNumber) {
for (int i = 0; i list = new ArrayList();
for (int i = 0; i list) {
list.add("1");
}
public void method3(ArrayList list) {
System.out.println(1);
list.remove(0);
}
}
class ThreadSafeSubClass extends ThreadSafe{
@Override
public void method3(ArrayList list) {
System.out.println(2);
new Thread(() -> {
list.remove(0);
}).start();
}
}
从这个例子可以看出 private 或 final 提供【安全】的意义所在,开闭原则中的【闭】
1.如果将method2和method3的访问修饰符改为public,不会出现线程安全问题,因为传参与上述代码中的list不是同一个对象
2.如果重现method3,则会出现线程安全问题,多个线程并发修改list
将方法设置为private可以防止子类重新method2和method3
将method1设置为final,可以防止public方法被重写
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net