Java中静态方法的线程安全问题

问题描述

Java中静态方法的线程安全问题

Java中多个线程同时访问一个静态方法是否存在线程安全问题?
比如以下代码,两个线程同时对方法中b修改不同的值,会有线程安全问题吗?
new Thread(new Runnable()
{
@Override
public void run()
{
fun(0);
}
}).start();

    new Thread(new Runnable()
    {

        @Override
        public void run()
        {
            fun(1);
        }
    }).start();
}

static void fun(int a)
{
    int b = 100;
    b = a;
    for (int i=0; i<100; i++)
        System.out.println(Thread.currentThread().getName() + "-->" + b);
}

解决方案

是不是线程安全取决于这个静态方法内部是否调用了线程不安全的方法,不安全的成员变量,并且没有同步。
如果没有调用任何方法,只调用局部变量,就是安全的。

解决方案二:

但静态方法不是内存中只有一份,而且方法中的局部变量也只有一份?

解决方案三:

静态方法不存在线程安全问题,只有涉及到全局共享变量的操作时才需要关注线程安全问题。

解决方案四:

总的结论: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.exit(0);
}
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”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。这个其实就是操作系统中的用信号量实现进程的互斥与同步问题,如果涉及在同一个类中有多个静态方法中处理多线程共享数据的话,那就变成用信号量解决生产者-消费者问题。也就是说,静态方法是一份临界资源,对静态方法的访问属于进入临界区;对静态变量的修改是一份临界资源,对静态变量的修改属于进入临界区。

时间: 2024-10-22 08:37:26

Java中静态方法的线程安全问题的相关文章

java中简单的线程问题

问题描述 java中简单的线程问题 我只创建了一个对象,为何运行的时候会有两个第五张票出现?即便是把上面的tickets变量加上static也是同样的结果 解决方案 加不加static与是否会发生多线程冲突无关,你需要在执行代码处加个锁防止多个线程同时调用一个对象 解决方案二: 源代码发我一下,多线程就是指多个对象对某个RUN方法多次调用. 解决方案三: 线程同步问题吧... 最好贴个代码出来. 解决方案四: 如果不想修改代码,也找不到错误,那么就在要执行逻辑代码之前去下重呗,虽然这样不合理.

java中panel实现线程接口以后,要调用repaint函数时,不进run函数

问题描述 java中panel实现线程接口以后,要调用repaint函数时,不进run函数 具体情况是 mypanel类实现了线程接口,在run函数中定义了sleep(100)后调用repaint函数,做一个小坦克游戏,在repaint之前要判断是否击中坦克,击中后要显示三张图片来体现爆炸效果,可是经过调试发现,每次第一次击中的时候,都是直接好多次repaint,没有休眠,后来发现根本就没有进mypanel的run()方法,好像有另一个其他线程再调用paint.这是怎么回事?感谢大家了 pack

对象-Java中静态方法在内存的位置?

问题描述 Java中静态方法在内存的位置? 最近在复习JAVA的内存知识.静态变量是加载在方法区的,内存分配即在方法区,当创建类的时候堆里有静态变量的指向地址,然后栈内的对象可以通过堆内地址找到该静态变量,为大家共用. 那静态方法呢?也是在方法区中分配内存吗?是随着类的加载而初始化的吗?但是我测试的如下运行代码,却并没有运行此静态方法. package com.learn.java.cc; class Static1{ static void sys1(){ System.out.println

多线程-JAVA中如何在一个线程里面停掉另一个线程

问题描述 JAVA中如何在一个线程里面停掉另一个线程 JAVA中如何在一个线程里面停掉另一个线程,是在一个线程里面哦 PS:新人第一次问问题,希望大神求教 解决方案 定义一个共享变量在run方法里面 while(start){ } 解决方案二: 所以说,这个问题的答案是:不可以! 虽然的确有那么个方法可以摧毁别人的线程,但很早很早就已经过期了,如果没记错的话我之前也这么玩,而结果是这个过期的方法根本没有作用-- 虽然说办法不是没有,那就是通过改变变量值,它知道自己该死了,就退出循环,走向结束.

java 中静态方法

问题描述 关于静态方法有些疑问,请高手们指点一下:比如:有一个工具类Util.java,里面有一个方法和成员变量,都是static 类型. public static HashMap<String, String> orgMap = new HashMap<String, String>();protected void buildMap(String MapDataPath){    orgMap.clear();    .......} 另外有三个java 文件要使用这个工具类

Java中定时启动线程

这里提供两种在指定时间后启动线程的方法.一是通过java.util.concurrent.DelayQueue实现:二是通过java.util.concurrent.ScheduledThreadPoolExecutor实现. 1. java.util.concurrent.DelayQueue 类DelayQueue是一个无界阻塞队列,只有在延迟期满时才能从中提取元素.它接受实现Delayed接口的实例作为元素. <<interface>>Delayed.java package

java中通用的线程池实例代码_java

复制代码 代码如下: package com.smart.frame.task.autoTask; import java.util.Collection;import java.util.Vector; /** * 任务分发器 */public class TaskManage extends Thread{    protected Vector<Runnable> tasks = new Vector<Runnable>();    protected boolean run

C#中Queue的线程安全问题

通过集合枚举在本质上不是一个线程安全的过程.甚至在对集合进行同步处理时,其他线程仍可以修改该集合,这会导致枚举数引发异常.若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常. 下列示例说明如何同步 Queue.如何确定 Queue 是否同步以及如何使用同步的 Queue.Unity3D教程手册 using System; using System.Collections; public class SamplesQueue { public s

Java中静态方法不具有多态性

class A1 {  public static void f() {   System.out.println("A1.f()");  } } class A2 extends A1 {  public static void f() {   System.out.println("A2.f()");  } } class T {  public static void main(String[] args) {   A1 a1 = new A1();   A1