共享是指一个变量可以被多个线程访问。
可变是指变量的值在其生命周期内可以改变。
对于一个对象是否应该是线程安全的取决于它是否被多线程访问。线程安全的这个性质,取决于程序中如何使用对象,而不是对象完成了什么。保证对象的线程安全性需要使用同步来协调对其可变状态的访问,若是做不到这点,就会导致脏读数据和其他不可预期的后果。
无论何时,只要有多于一个线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步协调线程对该变量的访问。使用synchronized关键字。
在没有正确同步的情况下,如果多个线程访问了同一个变量,你的程序就存在隐患。
有3中方式修复它:
1.不要跨越线程共享变量
2.使状态变量为不可变
3.在任何访问状态变量时使用同步
线程安全的定义一个类是线程安全的,是指在被多个线程访问时,类可以持续进行正确的行为。
对于线程安全类的实例进行顺序或并发的一系列操作,都不会导致实例处于无效状态。
示例:一个无状态的StatelessFactorizer,无状态的对象永远是线程安全的。
public class StatelessFactorizer
{
public void service()
{
int i=0;
i++;
System.out.println(i);
}
public static void main(String[] args)
{
StatelessFactorizer sf = new StatelessFactorizer();
//模拟多线程访问
for(int i=0; i<1000 ;i++)
{
new Thread(new Assist2(sf)).start();
}
}
}
//辅助线程
class Assist2 implements Runnable
{
private StatelessFactorizer sf;
public Assist2(StatelessFactorizer sf)
{
this.sf = sf;
}
@Override
public void run()
{
sf.service();
}
}
原子性:
示例UnsafeCountingFactorizer
public class UnsafeCountingFactorizer
{
private int count;
public long getCount()
{
return this.count;
}
public void service()
{
try
{ //对于线程的不好测试,在这里模拟阻塞,线程的阻塞是不会影响线程的安全
Thread.sleep(300);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
this.count++;
}
public static void main(String[] args)
{
UnsafeCountingFactorizer ucf = new UnsafeCountingFactorizer();
//模拟多线程访问
for(int i=0; i<300; i++)
{
new Thread(new Assist3(ucf)).start();
}
}
}
//辅助线程
class Assist3 implements Runnable
{
private UnsafeCountingFactorizer ucf;
public Assist3(UnsafeCountingFactorizer ucf)
{
this.ucf = ucf;
}
@Override
public void run()
{
this.ucf.service();
System.out.println(this.ucf.getCount());
}
}
示例:一个惰性初始化
public class LazyInitRace
{
private LazyInitRace instance = null;
public LazyInitRace getInstance()
{
if(instance == null)
{
//模拟线程延迟
try
{
Thread.sleep(5);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
instance = new LazyInitRace();
}
return instance;
}
public static void main(String[] args)
{
LazyInitRace lir = new LazyInitRace();
//模拟多线程
for(int i=0; i<50; i++)
{
new Thread(new Assist4(lir)).start();
}
}
}
//辅助线程
class Assist4 implements Runnable
{
private LazyInitRace lir;
public Assist4(LazyInitRace lir)
{
this.lir = lir;
}
@Override
public void run()
{
//通过hashCode来判断
System.out.println(this.lir.getInstance().hashCode());
}
}
解决方式1:
public synchronized LazyInitRace getInstance()
解决方式2:
/** * 效率比较高 */
if(instance == null)
{
synchronized(LazyInitRace.class)
{
if(instance == null)
instance = new LazyInitRace();
}
}
return instance;
在JDK中新增量java.util.concurrent.atomic包,里面包含了一些原子变量类,
这些类用来实现数字和对象引用的原子状态转换。
修改为下面:
private AtomicLong count;
public long getCount()
{
return this.count.get();
}
public void service()
{
this.count.getAndIncrement();
}
锁:
java提供了强制原子性的内置锁机制:synchronized块。
一个synchronized块有两个部分:所对象的引用和锁保护的代码块。
例如 synchronized(lock){
//访问或修改被锁保护的共享状态
}