什么是線程安全?這個(gè)首先要明確商虐。線程安全的類觉阅,值的是類內(nèi)共享的全局變量的訪問要保證在多線程下影響崖疤。由于多線程的訪問,修改典勇,便利讀取從而使這些變量結(jié)構(gòu)被破壞劫哼,或者針對(duì)這些變量的原子性被破壞,那么這個(gè)類就不是線程安全的割笙。
public class HelloThreadTest
{
public static void main(String[] args)
{
HelloThread r = new HelloThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class HelloThread implements Runnable
{
int i;
@Override
public void run()
{
while (true)
{
System.out.println("Hello number: " + i++);
try
{
Thread.sleep((long) Math.random() * 1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if (50 == i)
{
break;
}
}
}
}
程序的執(zhí)行結(jié)果是:順次打印了0到49的數(shù)字权烧,共50個(gè)數(shù)字。
這是因?yàn)樯烁龋琲是成員變量般码,則HelloThread的對(duì)象r只包含這一個(gè)i,兩個(gè)Thread對(duì)象因?yàn)橛蓃構(gòu)造乱顾,所以共享了同一個(gè)i板祝。
當(dāng)我們改變代碼如下時(shí)(原先的成員變量i被注釋掉,增加了方法中的局部變量i):
public class HelloThreadTest
{
public static void main(String[] args)
{
HelloThread r = new HelloThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class HelloThread implements Runnable
{
// int i;
// 若i是成員變量走净,則HelloThread的對(duì)象r只包含這一個(gè)i券时,兩個(gè)Thread對(duì)象因?yàn)橛蓃構(gòu)造,所以共享了同一個(gè)i
// 打印結(jié)果是0到49的數(shù)字
@Override
public void run()
{
int i = 0;
// 每一個(gè)線程都會(huì)擁有自己的一份局部變量的拷貝
// 線程之間互不影響
// 所以會(huì)打印100個(gè)數(shù)字伏伯,0到49每個(gè)數(shù)字都是兩遍
while (true)
{
System.out.println("Hello number: " + i++);
try
{
Thread.sleep((long) Math.random() * 1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if (50 == i)
{
break;
}
}
}
}
由于局部變量對(duì)于每一個(gè)線程來說都是有自己的拷貝橘洞,所以各個(gè)線程之間不再共享一個(gè)變量,輸出結(jié)果為100個(gè)數(shù)字说搅,實(shí)際上是兩組震檩,每組都是0到49的50個(gè)數(shù)字,并且兩組數(shù)字之間隨意地穿插在一起蜓堕。
得到的結(jié)論如下:
如果一個(gè)變量是成員變量抛虏,那么多個(gè)線程對(duì)同一個(gè)對(duì)象的成員變量進(jìn)行操作時(shí),它們對(duì)該成員變量是彼此影響的套才,也就是說一個(gè)線程對(duì)成員變量的改變會(huì)影響到另一個(gè)線程迂猴。
如果一個(gè)變量是局部變量,那么每個(gè)線程都會(huì)有一個(gè)該局部變量的拷貝(即便是同一個(gè)對(duì)象中的方法的局部變量背伴,也會(huì)對(duì)每一個(gè)線程有一個(gè)拷貝)沸毁,一個(gè)線程對(duì)該局部變量的改變不會(huì)影響到其他線程。