Java中对象的synchronized关键字

多线程共享同一储存空间,在带来方便的同时,也会造成访问冲突。Java语言提供了synchronized关键字已解决这种冲突,有效地避免了同一个数据对象被多个线程同时访问。使用synchronized关键字要注意一下几点:

  1. synchronized关键字可以作为函数的修饰符,也可以作为函数内的语句。synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)上。
  2. 无论synchronized关键字加在方法上还是加在对象上,它取得的锁都是对象,而不是一段代码或一个函数锁定,而且同步方法很可能还会被其他线程对象访问。
  3. 每个对象只有一把锁(lock)与之相关联。
  4. 实现同步是要以很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无所谓的同步控制。

synchronized关键字的作用域有两种。

  1. 某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法。如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其他线程不能同时访问这个对象中的任何一个synchronized方法。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其他线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。
  2. 某个类的范围中,synchronized static aStaticMethod(){}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

synchronized方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized方法都必须获得调用该类方法的类实例的锁方能执行,否则所属线程被阻塞,方法一旦执行,就独占该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有的声明为synchronized的成员函数中至少只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效地避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。

下面举个例子。一电影院有20张电影票要卖,目前有3个售票员。我们用sleep()函数来模拟售票,假设不使用synchronized关键字。

/**
 * 测试多线程情况下不使用synchronized关键字
 * @author QuLei
 *
 */
public class SellTest {
	public static void main(String[] args) {
		SellThread sellThread = new SellThread();
		 new Thread(sellThread,"售票员1").start();
		 new Thread(sellThread,"售票员2").start();
		 new Thread(sellThread,"售票员3").start();
	}
}

/**
 * 一电影院有20张电影票要卖,目前有3个售票员。我们用sleep()函数来模拟售票,
 * 假设不使用synchronized关键字。
 * @author QuLei
 *
 */
class SellThread implements Runnable{
	private int ticketCount = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(ticketCount > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"sell "+
						ticketCount--);
			}else {
				break;
			}
		}
	}
	
}


执行结果:
售票员2sell 2
售票员1sell 1
售票员3sell 0
售票员2sell -1

结果卖掉了22张票,如果使用synchronized关键字,就可以避免这种冲突,修改程序如下:

/**
 * 测试多线程情况下不使用synchronized关键字
 * @author QuLei
 *
 */
public class SellTest {
	public static void main(String[] args) {
		//SellThread sellThread = new SellThread();
		ThreadSell sellThread = new ThreadSell();
		 new Thread(sellThread,"售票员1").start();
		 new Thread(sellThread,"售票员2").start();
		 new Thread(sellThread,"售票员3").start();
	}
}

/**
 * 一电影院有20张电影票要卖,目前有3个售票员。我们用sleep()函数来模拟售票,
 *使用synchronized关键字。
 * @author QuLei
 *
 */
class ThreadSell implements Runnable{
	private int ticketCount = 20;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			synchronized (this) {
				if(ticketCount > 0 ) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+
							"sell "+ticketCount--);
				}else {
					break;
				}
			}
		}
	}
	
}

执行的结果为:
售票员3sell 5
售票员2sell 4
售票员2sell 3
售票员3sell 2
售票员1sell 1

这样就卖掉了20张票,这里锁的this即为一个对象,也可以锁一个其它的对象,这个对象被当做是一个标志位,这个标志位可以被任何一个售票员使用,这样售票员1线程拿到了其它的售票员线程发现以后就会被搁置,一直等到售票员1释放掉标志对象。

这里有两个容易误解的地方:

  1. 一个线程拿到synchronized括号中的对象后,其它也需要拿到这个对象才能运行的线程不能执行了。其实其它线程也是可以执行的,但他们执行到了需要synchronized中对象的时候,发现对象的标志位不可用,只能又被搁置了,所以synchronized使线程同步的时候是以牺牲效率为代价的。
  2. 一个线程拿到了synchronized括号中的对象之后,其他任何线程都不能执行了,假如其他不需要synchronized的对象才能执行的线程,还是可以和拿到synchronized的括号中的对象的线程一起运行的。有的方法在前面被加上了synchronized,其实这个时候就是把这个方法的调用者即this的标志位置0了,这样就不能和其他需要this才能运行的线程一起执行,但可以和其他不需要这个this对象的线程一起运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值