如何用sychronized来让线程同步。
多线程一旦操作同一块内存的数据,就可能造成数据的混乱,也就是常说的线程安全。
Java针对这样的问题,采用sychronized关键字来帮助开发者解决这样的问题。那么sychrnized是怎么使用的呢,下面我来讲解一下。
我先给出一个出现线程安全问题的代码:
- Class MyThread extends Thread {
- public static int index; //静态变量index
- public void run() {
- for(int i = 0;i<100;i++) {
- //循环打印index加1以后的值
- System.out.println(getName()+":"+index++);
- }
- }
- }
- public class SyncTest {
- public static void main(String[] args) {
- new MyThread().start();
- new MyThread().start();
- new MyThread().start();
- }
- }
复制代码 以上代码中,3个线程都会去访问一个静态的变量index,由于它们获取系统时间片的时刻是不确定的,
因此它们对index的访问和修改总是穿插着的,也就会造成类似于以下的输出结果:
- Thread-0 : 0
- Thread-2 : 1
- Thread-0 : 2
- Thread-2 : 3
- Thread-0 : 4
- Thread-0 : 5
- Thread-1 : 6
- Thread-2 : 7
复制代码 通过以上的运行结果可以看出,线程之间并没有等谁执行完以后再执行,而是交织着执行的,这不符合程序的本意。
这就出现了一个好办法,就是让它们可能会出现混乱的代码同步,也就是线程之间依次排队获取资源。
在Java中,使用sychronized关键字来保证线程的同步。sychronized的工作原理是这样的:
每个对象都可以有一个线程锁,sychronized可以用任何一个对象的线程锁来锁住一段代码,
任何想进入该段代码的线程必须在解锁以后才能继续执行,否则就进入等待状态。
其中,只有等占用该锁资源的线程执行完毕以后,该锁资源才会被释放。
例如,以上程序代码中的MyThread类可以这样来写,就可以很好地解决冲突的问题:
- class MyThread 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()方法,也需要在获得锁的情况下,才能继续运行。
另外,sysnchronized还可以加在成员方法上,这就叫做同步方法,此时的锁是加在this所引用得对象上的。
加在方法上面,还可以达到把代码段进行进一步细分的作用,也免去了需要找一个对象来加锁的步骤。
注意:不论是同步代码块,还是同步方法,它们都会损失一些效率。
因此,开发者应该根据线程可能出现问题的地方加上同步,
而不必大段大段的加上同步锁,这样可能是程序的性能降低。
总结:
sychronized关键字代表要为某一段代码加上一个同步锁,这样的锁是绑定在一个对象上边的。
如果是同步代码块,需要为该sychronized关键字提供一个对象的引用;
如果是同步方法,只需要加一个sychronized关键字的修饰。
|