java锁synchronized学习(一)

 

Java锁机制

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行

在java中,synchronized关键字用来控制线程同步,在多线程的环境下,控制synchronized代码段不被多个线程同时执行

线程的同步解决方法:

1.在需要同步的方法的方法签名中加入synchronized关键字

2.使用synchronized块对需要同步的代码段进行同步

3.使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象

另外,为了解决多个线程对同一变量进行访问时可能发生的安全性问题,我们不仅可以采用同步机制,更可以通过JDK 1.2中加入的ThreadLocal来保证更好的并发性

 

synchronized修饰的对象:

1.修饰一个代码块,被修饰的代码块称为同步语句块,作用范围是{}括起来的代码,作用对象时调用这个代码块的对象

2.修饰一个方法,被修饰的方法叫做同步方法,其作用范围是整个方法,作用的对象时调用这个方法的对象

3.修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象

4.修改一个类,其作用范围是synchronized后面括起来的部分,作用的对象是这个类的所有对象

一、线程的先来后到

多线程的线程同步机制实际上是靠锁的概念来控制的

从JVM的角度看锁的概念

在Java程序运行时环境中,JVM需要对两类线程共享的数据进行协调:

1.保存在堆中的实例变量

2.保存在方法区中的类变量

这两类数据是被所有线程共享的

(程序不需要协调保存在Java 栈当中的数据。因为这些数据是属于拥有该栈的线程所私有的)

在java虚拟机中,每个对象和类在逻辑上都是和一个监视器关联的

对于对象来说,相关联的监视器保护对象的实例变量

对类来说,监视器保护类变量

(如果一个对象没有实例变量,或者一个类没有变量,相关联的监视器就什么也不监视) 

为了实现监视器的排他性监视能力,java虚拟机为每一个对象和类都关联一个锁。代表任何时候只允许一个线程拥有的特权。线程访问实例变量或者类变量不需锁

但是如果线程获取了锁,那么在它释放这个锁之前,就没有其他线程可以获取同样数据的锁了。(锁住一个对象就是获取对象相关联的监视器)

类锁实际上用对象锁来实现。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的是那个类的Class对象。

一个线程可以多次对同一个对象上锁。对于每一个对象,java虚拟机维护一个加锁计数器,线程每获得一次该对象,计数器就加1,每释放一次,计数器就减 1,当计数器值为0时,锁就被完全释放了。

编程人员不需要自己动手加锁,对象锁是java虚拟机内部使用的

在java程序中,只需要使用synchronized块或者synchronized方法就可以标志一个监视区域。当每次进入一个监视区域时,java 虚拟机都会自动锁上对象或者类

当一个有限的资源被多个线程共享的时候,为了保证对共享资源的互斥访问,我们一定要给他们排出一个先来后到。而要做到这一点,对象锁在这里起着非常重要的作用

run方法还是加了一个synchronized关键字的,按道理说,这些线程应该可以一个接一个的执行这个run方法才对,事实上并没有按顺序执行,一样出现了乱序

对于一个成员方法加synchronized关键字,这实际上是以这个成员方法所在的对象本身作为对象锁。在本例中,就是 以ThreadTest类的一个具体对象,也就是该线程自身作为对象锁的。一共十个线程,每个线程持有自己 线程对象的那个对象锁。这必然不能产生同步的效果。换句话说,如果要对这些线程进行同步,那么这些线程所持有的对象锁应当是共享且唯一的

该程序通过在main方法启动10个线程之前,创建了一个String类型的对象。并通过ThreadTest2的构造函数,将这个对象赋值 给每一个ThreadTest2线程对象中的私有变量lock。根据Java方法的传值特点,我们知道,这些线程的lock变量实际上指向的是堆内存中的 同一个区域,即存放main函数中的lock变量的区域

程序将原来run方法前的synchronized关键字去掉,换用了run方法中的一个synchronized块来实现。这个同步块的对象锁,就是 main方法中创建的那个String对象。换句话说,他们指向的是同一个String类型的对象,对象锁是共享且唯一的于是,我们看到了预期的效果:10个线程不再是争先恐后的报数了,而是一个接一个的报数

这段代码没有使用main方法中创建的String对象作为这10个线程的线程锁。而是通过在run方法中调用本线程中一个静态的同步 方法abc而实现了线程的同步

对于同步静态方法,对象锁就是该静态方法所在的类的Class实例,由于在JVM中,所有被加载的类都有唯一的类对象,具体到本例,就是唯一的 ThreadTest3.class对象。不管我们创建了该类的多少实例,但是它的类实例仍然是一个

 

1、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作

2、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的
Class对象(唯一)

3、对于代码块,对象锁即指synchronized(abc)中的abc

4、因为第一种情况,对象锁即为每一个线程对象,因此有多个,所以同步失效,第二种共用同一个对象锁lock,因此同步生效,第三个因为是static因此对象锁为ThreadTest3的class 对象,因此同步生效

则同步有两种方式,同步块和同步方法

如果是同步代码块,则对象锁需要编程人员自己指定,一般有些代码为synchronized(this)只有在单态模式才生效

如果是同步方法,则分静态和非静态两种 

静态方法则一定会同步,非静态方法需在单例模式才生效,推荐用静态方法(不用担心是否单例)

synchronized关键字实际上是依靠对象锁的机制来实现线程同步的

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值