多线程一旦操作一块内存,就可能造成数据的混乱,也就是常说的线程安全问题。Java针对这样的问题,采用sychronized关键字来解决这样的问题。那么sychronized是怎么使用的呢??
首先,给出一段会出现线程安全问题的代码:
package thread;
class MyThread extends Thread{
public static int index;
public void run(){
for(int i=0;i<100;i++){
System.out.println(getName()+":"+index++);
}
}
}
package thread;
public class SyncTest{
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
以上代码中,3个线程都会访问一个静态的变量index,由于它们获取系统时间片的时刻是不确定的,因此它们对index的访问和修改总是穿插着的,也就是会造成类似以下的输出结果:
Thread-0:32
Thread-0:33
Thread-2:34
Thread-0:35
Thread-1:36
Thread-2:37
Thread-0:38
Thread-1:39
Thread-2:40
。。。。
通过以上运行结果可以看出,线程之间并没有等谁执行完以后再执行,而是交织着执行的,这不符合程序的本意。有一个好办法,就是让它们可能出现混乱的代码同步。也就是在线程之间依次排队获取资源。
在java中,使用sychronized关键字来保证线程的同步。Synchronized的工作原理是这样的:每个对象都可以有一个线程锁,synchronized可以用任何一个对象的线程锁来锁住一段代码,任何想进入该段代码的线程必须在解锁后才能继续执行,否则就进入等待状态。其中,只有等占有该资源的线程执行完毕以后,该锁资源才会被释放。如,以上的MyThread类可以这样写,就可以很好的解决冲突问题:
package thread;
public class MyThread2 extends Thread{
public static int index;
public static Object obj=new Object();
public void run(){
synchronized(obj){
for(int i=0;i<100;i++){
System.out.println(getName()+":"+index++);
}
}
}
}
以上代码中,把for循环的代码块上加上obj对象的锁,当一个线程进入该段代码以后,即使其他线程进入run()方法,也需要在获得锁的情况下才能继续运行。