Java多线程并发去调⽤⼀个类的静态⽅法,会有问题吗?
总的结论:java是线程安全的,即对任何⽅法(包括静态⽅法)都可以不考虑线程冲突,但有⼀个前提,就是不能存在全局变量。如果存在全局变量,则需要使⽤同步机制。
如下通过⼀组对⽐例⼦从头讲解:
在多线程中使⽤静态⽅法会发⽣什么事?也就是说多线程访问同⼀个类的static静态⽅法会发⽣什么事?是否会发⽣线程安全问题?
public class Test {
public static void operation(){
// ... do something
}
}
事实证明只要在静态函数中没有处理多线程共享数据,就不存在着多线程访问同⼀个静态⽅法会出现资
源冲突的问题。下⾯看⼀个例⼦:
public class StaticThread implements Runnable {
@Override
public void run(){
// TODO Auto-generated method stub
StaticAction.print();
}
public static void main(String[] args){
for(int i =0; i <100; i++){
new Thread(new StaticThread()).start();
}
}
}
public class StaticAction {
public static int i =0;
public static void print(){
int sum =0;
for(int i =0; i <10; i++){
System.out.print("step "+ i +" is running.");
sum += i;
}
if(sum !=45){
System.out.println("Thread error!");
}
System.out.println("sum is "+ sum);
}
}
实际执⾏的结果显⽰各个线程对静态⽅法的访问是交叉执⾏的,但是这并不影响各个线程静态⽅法print()中sum值的计算。也就是说,在此过程中没有使⽤全局变量的静态⽅法在多线程中是安全的,静态⽅法是否引起线程安全问题主要看该静态⽅法是否对全局变量(静态变量static member)进⾏修改操作。
在多线程中使⽤同⼀个静态⽅法时,每个线程使⽤各⾃的实例字段(instance field)的副本,⽽共享⼀个静态字段(static field)。所以说,如果该静态⽅法不去操作⼀个静态成员,只在⽅法内部使⽤实例字段(instance field),不会引起安全性问题。
但是,如果该静态⽅法操作了⼀个静态变量,则需要静态⽅法中采⽤互斥访问的⽅式进⾏安全处理。我们来看⼀下没有使⽤互斥访问的话会产⽣怎样的问题:
public class StaticAction {
public static int i =0;
public static void incValue(){
int temp = StaticAction.i;
try{
Thread.sleep(1);
}catch(Exception e){
e.printStackTrace();
}
temp++;
StaticAction.i = temp;
}
}
public class StaticThread implements Runnable {
@Override
public void run(){
// TODO Auto-generated method stub
StaticAction.incValue();
}
public static void main(String[] args){
for(int i =0; i <100; i++){
new Thread(new StaticThread()).start();
}
try{
Thread.sleep(1000);//预留⾜够的时间让上⾯的线程跑完
}catch(Exception e){
e.printStackTrace();
}
System.out.println(StaticAction.i);一个线程可以包含多个进程
}
}
实际运⾏结果显⽰i值为随机的数字。为了实现互斥访问,这时我们需要加⼊⼀个synchronized关键字。代码修改如下:
public class StaticAction {
public static int i =0;
public synchronized static void incValue(){
int temp = StaticAction.i;
try{
Thread.sleep(1);
}catch(Exception e){
e.printStackTrace();
}
temp++;
StaticAction.i = temp;
}
}
public class StaticThread implements Runnable {
@Override
public void run(){
// TODO Auto-generated method stub
StaticAction.incValue();
}
public static void main(String[] args){
for(int i =0; i <100; i++){
new Thread(new StaticThread()).start();
}
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(StaticAction.i);
}
}
运⾏结果则必然是100。
加⼊synchronized关键字的静态⽅法称为同步静态⽅法。
在访问同步静态⽅法时,会获取该类的“Class”对象,所以当⼀个线程进⼊同步的静态⽅法中时,线程监视器获取类本⾝的对象锁,其它线程不能进⼊这个类的任何静态同步⽅法。它不像实例⽅法,因为多个线程可以同时访问不同实例同步实例⽅法。这个其实就是操作系统中的⽤信号量实现进程的互斥与同步问题,如果涉及在同⼀个类中有多个静态⽅法中处理多线程共享数据的话,那就变成⽤信号量解决⽣产者-消费者问题。也就是说,静态⽅法是⼀份临界资源,对静态⽅法的访问属于进⼊临界区;对静态变量的修改是⼀份临界资源,对静态变量的修改属于进⼊临界区。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论