实际开发中,使用多线程程序的情况很多。以机场售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行售票功能,但是当两个线程同时访问这段代码时(假设只剩一张票),第一个线程得出票数为1,第二个线程同样得出票数为1 这样就会产生负数。实际上线程安全问题来源于两个线程同时存取单一对象的数据而造成的。
例如:
class ThreadSafeTest implements Runnable{
// 设置当前总票数
int num = 10;
public void run(){
while(true){
if(num > 0){
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("tickets:" + num--);
}
}
}
public static void main(String[] args) {
// 实例化对象
ThreadSafeTest t = new ThreadSafeTest();
// 以该对象分别
Thread tA = new Thread(t);
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
// 分别启动线程
tA.start();
tB.start();
tC.start();
tD.start();
}
}
运行本示例后,结果如下:
从运行结果来看,最后打印剩下的的票数为负数,这样显然是错误的。
线程同步机制
1.同步块
在java中提供了同步机制,可以有效地防止资源冲突。同步机制使用关键字synchronized。
例如:
class ThreadSafeTest implements Runnable{
// 设置当前总票数
int num = 10;
public void run(){
while(true){
// 使用同步机制,把num的操作代码放在同步块中
synchronized (""){
if(num > 0){
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("tickets:" + num--);
}
}
}
}
public static void main(String[] args) {
// 实例化对象
ThreadSafeTest t = new ThreadSafeTest();
// 以该对象分别
Thread tA = new Thread(t);
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
// 分别启动线程
tA.start();
tB.start();
tC.start();
tD.start();
}
}
运行本示例后,结果如下:
从图中可以看出打印到最后票的数量没有出现负数,这是因为将资源放在了同步块中,这个同步块也叫做临界区,语法如下:
synchronized(Object){
}
2.同步方法
同步方法就是在方法前面修饰synchronized关键字的方法,其语法如下:
synchronized void f(){}
将共享资源的操作放在同步方法中,运行结果与使用同步块的结果一致。
例如:
class ThreadSafeTest implements Runnable{
// 设置当前总票数
int num = 10;
public synchronized void doit(){
if(num > 0){
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("tickets:" + num--);
}
}
public void run(){
while(true){
doit();
}
}
public static void main(String[] args) {
// 实例化对象
ThreadSafeTest t = new ThreadSafeTest();
// 以该对象分别
Thread tA = new Thread(t);
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
// 分别启动线程
tA.start();
tB.start();
tC.start();
tD.start();
}
}
运行本示例后,结果如下: