⑴ java 靜態塊 線程問題
想了很久,終於弄明白為什麼了。
靜態塊確實是在main之前運行,但這不是關鍵。 關鍵在於類的初始化過程。
類的初始化,包括靜態賦值如 static boolean initialized = false; 和靜態初始化塊, static { ... }, 按照聲明的順序依次執行。某個線程在對某個類進行初始化的時候,會先給這個類加鎖,直到初始化全部完成之後才釋放鎖。 你的代碼存在死鎖問題。
把代碼改成這樣,看看輸出是什麼:
public class HelloWorld {
private static boolean initialized = false;
static Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("aaaa");
System.out.println(initialized); // label
System.out.println("Hello ");
initialized = true;
}
});
static {
t.start();
}
static {
try {
Thread.sleep(500);
}
catch(Exception e) {
}
System.out.println("bbbb");
}
public static void main(String[] args) {
System.out.println(" world!");
}
}
你會發現輸出是這樣的:
aaaa
bbbb
false ....
注意,bbbb總是在false之前列印,不管sleep多長時間。
而在run方法里,列印aaaa之後應該緊接著列印false才對,為什麼還要等待睡覺中的bbbb呢?
虛擬機在執行 main之前,先要載入HelloWorld類,那麼mian線程會對HelloWorld的Class加鎖,然後依次執行
static boolean initialized = false;
static Thread t = ...
static { t.start(); }
static { sleep(500); print "bbbb"; }
執行完這些語句,才會把鎖釋放。
在這之間,啟動了t線程,t先列印aaaa,然後試圖列印initialized,但是這時候HelloWorld的Class被鎖定了,所以t線程必須等待main線程把鎖釋放掉,也就是上面所有的static語句執行完畢。等main線程執行完了bbbb,就會把鎖釋放掉,然後t線程就可以繼續執行了。
如果加入了join語句,那麼main線程就會等待t線程,但是t線程試圖訪問initialized的時候必須等待main線程,這就導致了死鎖。你把label語句去掉之後,就不需要等到main線程了,所以會把hello列印出來。
⑵ java中多線程中的static和voilate
static不能實現多線程共享,它的作用是讓同一個類的不同對象,共用同一個變數,與線程無關。如果有多個線程同時修改一個靜態變數,同樣會有不同步問題。
⑶ Java:多線程使用一個靜態變數容易發生線程安全問題,該如何解決呢
synchronized
⑷ java多線程調用靜態方法安全嗎
這要看靜態方法中有沒有操作靜態成員變數了,看代碼吧
{
staticintnum=0;
publicstaticvoidaddOne()throwsInterruptedException{
for(inti=0;i<=10;i++){
num+=i;
}
System.out.println(Thread.currentThread().getName()+"-"+num);
num=0;
}
}
如上面代碼所示,當單線程運行情況下是輸出正常的,num最後應該輸出55,但是在多線程情況下,則會出現每個線程輸出的num不一樣的情況,這是因為num是靜態成員變數,為多個線程所共享的,有可能會出現一個線程的num值還沒有重新賦值為0,另外一個線程已經讀取到num的累加後的值,所以說,多線程情況下,靜態方法中如果操作靜態成員變數,那這個靜態方法就不是線程安全的
⑸ java多線程高手進!!!靜態方法只對當前線程有效,解釋一下原理
靜態方法是類級別的東西,和實例完全不同,不在同一個內存空間中。不太清楚你到底想問什麼。。。比如你說的sleep,是個native的方法,暫時看不到源碼,猜測大致實現為Thread.currentThread()獲取當前線程,然後再對當前線程操作。
⑹ Java:多線程與靜態方法
Function調用其靜態方法獲取實例,
靜態方法不會創建新的實例,所以五個線程共享Function的實例
⑺ java如何實現靜態變數多線程安全問題
public class A { public void method01(){ //dosomething } public void method02(){ //dosomething } //public void method03(){ public static void method03(){ // 靜態方法里才可以有今天變數 static Map map = new TreeMap(); static String x=「」; // 如果你的 method4,5,6是非同步的, 5,6得到的值就是不確定的, // 有可能是4之前的值,也有可能是4的賦值 method4(){ 這裡面會給map和x賦值 } method5(){ 這裡面會用到map和x } method6(){ 這裡面會用到map和x } }}
⑻ java 線程池 定義為靜態時 線程出現問題
說明1-5-thread-1這個線程與1-5-thread-6在並發執行,它也在列印日誌
⑼ 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.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」對象,所以當一個線程進入同步的靜態方法中時,線程監視器獲取類本身的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,因為多個線程可以同時訪問不同實例同步實例方法。這個其實就是操作系統中的用信號量實現進程的互斥與同步問題,如果涉及在同一個類中有多個靜態方法中處理多線程共享數據的話,那就變成用信號量解決生產者-消費者問題。也就是說,靜態方法是一份臨界資源,對靜態方法的訪問屬於進入臨界區;對靜態變數的修改是一份臨界資源,對靜態變數的修改屬於進入臨界區。
⑽ 關於java線程中static關鍵字
有要詳解
static表示「全局」或者「靜態」的意思,用來修飾成員變數和成員方法,也可以形成靜態static代碼塊,但是Java語言中沒有全局變數的概念。
被static修飾的成員變數和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共享。
只要這個類被載入,Java虛擬機就能根據類名在運行時數據區的方法區內定找到他們。因此,static對象可以在它的任何對象創建之前訪問,無需引用任何對象。
用public修飾的static成員變數和成員方法本質是全局變數和全局方法,當聲明它類的對象市,不生成static變數的副本,而是類的所有實例共享同一個static變數。
static變數前可以有private修飾,表示這個變數可以在類的靜態代碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用--廢話),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上你需要搞明白,private是訪問許可權限定,static表示不要實例化就可以使用,這樣就容易理解多了。static前面加上其它訪問許可權關鍵字的效果也以此類推。
static修飾的成員變數和成員方法習慣上稱為靜態變數和靜態方法,可以直接通過類名來訪問,訪問語法為:
類名.靜態方法名(參數列表...)
類名.靜態變數名
用static修飾的代碼塊表示靜態代碼塊,當Java虛擬機(JVM)載入類時,就會執行該代碼塊(用處非常大,呵呵)。
1、static變數
按照是否靜態的對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數或類變數;另一種是沒有被static修飾的變數,叫實例變數。
兩者的區別是:
對於靜態變數在內存中只有一個拷貝(節省內存),JVM只為靜態分配一次內存,在載入類的過程中完成靜態變數的內存分配,可用類名直接訪問(方便),當然也可以通過對象來訪問(但是這是不推薦的)。
對於實例變數,沒創建一個實例,就會為實例變數分配一次內存,實例變數可以在內存中有多個拷貝,互不影響(靈活)。
所以一般在需要實現以下兩個功能時使用靜態變數:
在對象之間共享值時
方便訪問變數時
2、靜態方法
靜態方法可以直接通過類名調用,任何的實例也都可以調用,
因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的實例變數和實例方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。
因為實例成員與特定的對象關聯!這個需要去理解,想明白其中的道理,不是記憶!!!
因為static方法獨立於任何實例,因此static方法必須被實現,而不能是抽象的abstract。
例如為了方便方法的調用,Java API中的Math類中所有的方法都是靜態的,而一般類內部的static方法也是方便其它類對該方法的調用。
靜態方法是類內部的一類特殊方法,只有在需要時才將對應的方法聲明成靜態的,一個類內部的方法一般都是非靜態的
3、static代碼塊
static代碼塊也叫靜態代碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的代碼塊,如果static代碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個代碼塊只會被執行一次。例如:
public class Test5 {
private static int a;
private int b;
static{
Test5.a=3;
System.out.println(a);
Test5 t=new Test5();
t.f();
t.b=1000;
System.out.println(t.b);
}
static{
Test5.a=4;
System.out.println(a);
}
public static void main(String[] args) {
// TODO 自動生成方法存根
}
static{
Test5.a=5;
System.out.println(a);
}
public void f(){
System.out.println("hhahhahah");
}
}
運行結果:
3
hhahhahah
1000
4
5
利用靜態代碼塊可以對一些static變數進行賦值,最後再看一眼這些例子,都一個static的main方法,這樣JVM在運行main方法的時候可以直接調用而不用創建實例。
4、static和final一塊用表示什麼
static final用來修飾成員變數和成員方法,可簡單理解為「全局常量」!
對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。
對於方法,表示不可覆蓋,並且可以通過類名直接訪問。
有時你希望定義一個類成員,使它的使用完全獨立於該類的任何對象。通常情況下,類成員必須通過它的類的對象訪問,但是可以創建這樣一個成員,它能夠被它自己使用,而不必引用特定的實例。在成員的聲明前面加上關鍵字static(靜態的)就能創建這樣的成員。如果一個成員被聲明為static,它就能夠在它的類的任何對象創建之前被訪問,而不必引用任何對象。你可以將方法和變數都聲明為static。static 成員的最常見的例子是main( ) 。因為在程序開始執行時必須調用main() ,所以它被聲明為static。
聲明為static的變數實質上就是全局變數。當聲明一個對象時,並不產生static變數的拷貝,而是該類所有的實例變數共用同一個static變數。聲明為static的方法有以下幾條限制:
•
它們僅能調用其他的static 方法。
•
它們只能訪問static數據。
•
它們不能以任何方式引用this 或super(關鍵字super 與繼承有關,在下一章中描述)。
如果你需要通過計算來初始化你的static變數,你可以聲明一個static塊,Static 塊僅在該類被載入時執行一次。下面的例子顯示的類有一個static方法,一些static變數,以及一個static 初始化塊:
// Demonstrate static variables,methods,and blocks.
class UseStatic {
static int a = 3;
static int b;
static void meth(int x) {
System.out.println("x = " + x);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
static {
System.out.println("Static block initialized.");
b = a * 4;
}
public static void main(String args[]) {
meth(42);
}
}
一旦UseStatic 類被裝載,所有的static語句被運行。首先,a被設置為3,接著static 塊執行(列印一條消息),最後,b被初始化為a*4 或12。然後調用main(),main() 調用meth() ,把值42傳遞給x。3個println ( ) 語句引用兩個static變數a和b,以及局部變數x 。
注意:在一個static 方法中引用任何實例變數都是非法的。
下面是該程序的輸出:
Static block initialized.
x = 42
a = 3
b = 12
在定義它們的類的外面,static 方法和變數能獨立於任何對象而被使用。這樣,你只要在類的名字後面加點號運算符即可。例如,如果你希望從類外面調用一個static方法,你可以使用下面通用的格式:
classname.method( )
這里,classname 是類的名字,在該類中定義static方法。可以看到,這種格式與通過對象引用變數調用非static方法的格式類似。一個static變數可以以同樣的格式來訪問——類名加點號運算符。這就是Java 如何實現全局功能和全局變數的一個控製版本。
下面是一個例子。在main() 中,static方法callme() 和static 變數b在它們的類之外被訪問。
class StaticDemo {
static int a = 42;
static int b = 99;
static void callme() {
System.out.println("a = " + a);
}
}
class StaticByName {
public static void main(String args[]) {
StaticDemo.callme();
System.out.println("b = " + StaticDemo.b);
}
}
下面是該程序的輸出:
a = 42
b = 99
static成員是不能被其所在class創建的實例訪問的。
如果不加static修飾的成員是對象成員,也就是歸每個對象所有的。
加static修飾的成員是類成員,就是可以由一個類直接調用,為所有對象共有的