关于synchronized关键字的

锁对象是什么?

       锁对象指的是被锁的对象。

使用锁的实质

  多线程执行枷锁的方法或代码的时候,会先判断使用的锁是否被占用,如果没有被占用就获取并使用该锁,其他需要该锁的线程就处于等待状态;如果占用就需要等待该锁被释放重新尝试获取锁对象,知道获取到锁对象才能执行后面的代码或者方法。锁应该理解成一张通行证,有才能继续走,没有就要等这张通行证

锁对象是一切的关键

使用锁的效果和怎么使用

代码段级别的锁对象,是可以随意定义的,可以是当前对象,可以是当前类的其他实例对象,可以是当前类的字节码文件,可以是其他任意引用对象。一旦锁住了锁对象,其他需要该锁对象的线程必须等待。

方法级别的同步锁(被synchronized修饰的方法),锁住的是对象。只要是使用该对象作为锁对象的代码都会产生互斥效果。
互斥:如果一个线程正在执行某一部分操作,那么其他线程就不可以再执行这部分操作。

Java使用关键字synchronized来执行线程的互斥处理

synchronized方法

如果声明一个方法的时候,在前面加上synchronized,那么这个方法就只能由一个线程运行。只能由一个线程运行是每次只能由一个线程运行的意思,并不是说仅能让某一特定线程运行。

同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以代替volatile功能),这点确实也是很重要

关于锁和监视
线程的互斥机制称为监视(monitor)。另外,获取锁有时也叫做“拥有(own)监视”或“持有(hold)锁”
当前线程是否已获取某一对象的锁可以通过Thread.holdsLock方法来确认。当前线程已获取对象obj的锁时,可使用assert来表示
assert Thread.holdsLock(obj);

synchronized的三种应用方式

synchronized关键字最主要有以下三种应用方式

  • 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰静态方法,作用于当前类对象加锁,进入同部代码前要获得当前类对象的锁
  • 修饰代码块,指定加锁对象,对给定对象枷锁,进入同步代码库前要获得给定对象的锁

synchronized作用于实例方法

所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法

public class AccountingSync implements Runnable{
	//共享资源(临界资源)
	static int  i = 0;
	/**
	* synchronized 修饰实例方法
	* /
    public synchronized void increase()
	{
		i++;
	}
	
	public void run()
	{
		for(int j = 0; j < 10000000;j ++)
		{
			increase1();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
	AccountingSync instance = new AccountingSync();
		Thread t1 = new Thread(new AccountingSyncBad(instance));
		Thread t2 = new Thread(new AccountingSyncBad(instance));
		
		t1.start();
		t2.start();
		
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

在上面的代码中,开启了两个线程操作同一个共享资源变量i,由于i++;操作并不具备原子性,该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分两部完成,如果第二个线程在第一个线程读取旧值和写回新值期间读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败,因此对于increase()方法必须使用synchronized进行修饰,以保证线程安全。

synchronized作用于静态方法

当synchronized作用于静态方法,其锁就是当前类的class对象锁。由于静态成员不转属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是当前类的class对象,而访问非静态synchronized方法重用的是当前对象锁

public class AccountingSyncClass implements Runnable{
 	static int i = 0;
 	/**
 	*作用于静态方法,锁是当前class对象,也就是
 	*AccountingSyncClass类对应的class对象
 	*/
	public static synchronized void increase()
	{
	    	i++;
	}
	public synchronized void increase4Obj()
	{
				i++;
	}

	public void run()
	{
			for(int j = 0; j < 10000000;j++){
					increase();
			}
	}
	public static void main(String[] args) throws InterruptedException {
        //new新实例
        Thread t1=new Thread(new AccountingSyncClass());
        //new心事了
        Thread t2=new Thread(new AccountingSyncClass());
        //启动线程
        t1.start();t2.start();

        t1.join();t2.join();
        System.out.println(i);

}

值得注意的是这里的静态Increase()方法,与修饰实例方法不同的是,它的锁的对象是当前类的class对象。 对于increase4Obj()方法的是实例方法,其中它的对象锁是当前实例对象,如果别的线程调用这个方法,不会产生互斥现象。但是应该注意到的是线程共享资源i,会产生线程安全问题。

synchronized同部代码块

除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,还存在比较耗时的操作,需要同部的代码又只有一小部分,这时候可以利用synchronized关键字对这部分代码进行加上互斥锁

public class AccountingSync implements Runnable{
		static AccountingSync instance  = new AccountingSync();
		static int i = 0;
	public void run(){
	synchronized(instance){
		for(int j = 0; j < 10000000; j ++)
		{
				i++;
}
}
}

public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(instance);
        Thread t2=new Thread(instance);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(i);
    }

}

转载博客:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值