Static与线程安全



Static与线程安全。先来说说Static的用法之后介绍线程安全的相关知识。

类(static)变量

在所有类的实例中共享

可以被标记为public或private

如果被标记为public而没有该类的实例,可以从该类的外部访问。

 

有时想有一个可以在类的所有实例中共享的变量。比如,这可以用作实例之间交流的基础或追踪已经创建的实例的数   量。

可以用关键字static来标记变量的办法获得这个效果。这样的变量有时被叫做class variable,以便与不共享的成员或实例变量区分开来。

Static变量在某种程度上与其它语言中的全局变量相似。Java编程语言没有这样的全局语言,但static变量是可以从类的任何实例访问的单个变量。

如果static变量没有被标记成private,它可能会被从该类的外部进行访问。要这样做,不需要类的实例,可以通过类名指向它。

类(static)方法

因为static方法不需它所属的类的任何实例就会被调用,因此没有this值。结果是,static方法不能访问与它本身的参数以及static变量分离的任何变量。访问非静态变量的尝试会引起编译错误。

 

也就是说static 的方法只能访问static的类。

 

public class Wrong {

int x;

public static void main(String args[]) {

x = 9; // COMPILER ERROR!

}

}

below is right

 

public class Wrong {

 

static int x;

 

public static void main(String args[]) {

x = 9; // COMPILER ERROR!

}

}

 

注意:

Main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。

静态方法不能被覆盖成非静态。

 

静态初始化程序

 

方法程序体中不存在的代码在static block中类可以包含该代码,这是完全有效的。当类被装载时,静态块代码只执行一次。类中不同的静态块按它们在类中出现的顺序被执行。


public class StaticInitDemo {

 

static int i = 5;

static {

System.out.println(“Static code i= “+ i++ );

}

}

public class Test {

public static void main(String args[]) {

System.out.println(“Main code: i=”

+ StaticInitDemo.i);

}

}

 

Static code: i=5

Main code: i=6

 

注意:

1.   Static方法和数据的单个(共享)副本是因为类和该类的所有实例而存在。通过一个实例或通过类本身可以访问static成员。

2.   非静态数据只限于实例,只能通过该实例的非静态方法对它进行访问。非静态数据定义对象之间互不相同的特点,非静态方法在它们所作用的非静态数据的基础上对每个对象的行为互不相同。

举一个简单例子说明使用:

一款鞋子的尺码是固定的,但这个型号的颜色可以有不同的,那么定义的时候,我们可以吧这款鞋子尺码定义成static的,颜色值定义成非static的,。颜色根据对象的不同而不同,其行为也根据对象的不同而不同,在它所作用的非静态数据的基础上对不同对象返回不同的颜色。

 

线程安全:

对于线程安全,我们先看两个例子:

class CheckoutLane {     public static float GetTotal(Cart cart)     {         float total = 0;

for (int i = 0; i < cart.GroceryItems.Length; i++)         {             total += cart.GroceryItems[i].Price;              Thread.Sleep(100);         }         return total;     }

}

对于上面的代码,你使用多少的线程来控制,都是安全的。因为线程之间,不会共享什么资源,唯一相同的是使用了同一个逻辑。其实这是在破坏线程的前题方面下功夫,出现线程不安全的条件都没有了,那肯定就是线程安全的。

class CheckoutLane {     static float total;

public static float GetTotal(Cart cart)     {         total = 0;

for (int i = 0; i < cart.GroceryItems.Length; i++)         {             total += cart.GroceryItems[i].Price;               Thread.Sleep(100);         }         return total;     }

}

对于上面的这个例子,不是线程安全的,因为共享了static float total;这个资源,而各个线程都随机都被调用,可以任意修改total这个数据。这个,就正如多个收银员共享柜台,任意执行收银操作一样。

class CheckoutLane {     static float total;     static object synchLock = new object();

public static float GetTotal(Cart cart)     {         lock(synchLock)         {             total = 0;

for (int i = 0; i < cart.GroceryItems.Length; i++)             {                 total += cart.GroceryItems[i].Price;                   Thread.Sleep(100);             }             return total;         }     } }

上面的代码是线程安全的,使用了lock关键字,可以达到一个线程强占资源的效果。这时,synchLock就作为了共享的资源,被某一个线程锁住了。