可重入(reentrant)的类或方法,又称线程安全(threadsafe)的类或方法。
是指一个类或方法,在多个线程里同时调用(并发调用)的时候,其功能仍然正常。
如在并发调用时功能出错的方法(线程不安全的Thread Unsafe) 例如,下面的方法用于求和 int sum(int n){ int result=0; for(int i=1;i<=n;i++){ result+=i; } return result; }
我们的代码:
class Add{ static int sum(int n){ int result=0; for(int i=1;i<=n;i++){ result+=i; } return result; } } public class Demo { public static void main(String[] args) { new Thread(new Runnable(){ @Override public void run(){ int n=500; while(n--!=0){ int ret=Add.sum(100); if(ret!=5050){System.out.println(Thread.currentThread().getName()+" "+ret);} } } },"t1").start(); new Thread(new Runnable(){ @Override public void run(){ int n=500; while(n--!=0){ int ret=Add.sum(100); if(ret!=5050){System.out.println(Thread.currentThread().getName()+" "+ret);} } } },"t2").start(); } }
运行时,方法sum始终能进行对1~100的求和,得出5050。所以是线程安全的。
我们从反面验证线程不安全的
将以上代码修改:
class Number{public static int result;} class Add{ static int sum(int n){ //借助全局变量,此方法在单线程时是没有任何问题的(可运行测试) Number.result=0; for(int i=1;i<=n;i++){ Number.result+=i; } return Number.result; } } public class Demo { public static void main(String[] args) { new Thread(new Runnable(){ @Override public void run(){ int n=500; while(n--!=0){ int ret=Add.sum(100); if(ret!=5050){System.out.println(Thread.currentThread().getName()+" "+ret);} } } },"t1").start(); new Thread(new Runnable(){ @Override public void run(){ int n=500; while(n--!=0){ int ret=Add.sum(100); if(ret!=5050){System.out.println(Thread.currentThread().getName()+" "+ret);} } } },"t2").start(); } }
当计算结果不是5050时我们打印输出
运行如图:
可重入的方法
判断一个方法是否是可重入的:
(1)在单线程的情况下,该方法表现正常
如果单线程也不行,说明这个方法写错了
(2) 在多线程并发调用此方法时,该方法仍然表现正常。
则称为该方法是可重入的。
以下方法很可能是不可重入的:
(1)一个全局方法(写在类体之外的方法)
如果它借助于全局对象来实现,并且有写操作,那么就是不可重入的。
(2) 一个类的成员方法
它访问并修改了成员变量,那么一般情况下它就是不可重入的。
如何将不可重入的方法,改为可重入的?
(1) 不借助外部的变量来实现
尽量用本方法内定义的局部变量来实现。
或者在本方法动态创建对象
(没有外部依赖,不操作外部变量)
(2) 实在不行的话,加上互斥锁控制
class Number{public static int result;} class Add{ static synchronized int sum(int n){ //synchronized将不可重入修改为可重入(加锁) Number.result=0; for(int i=1;i<=n;i++){ Number.result+=i; } return Number.result; } }