Java synchronized笔记-阿里云开发者社区

开发者社区> anmypeng1> 正文

Java synchronized笔记

简介:
+关注继续查看

1,造成线程安全问题的原因
①存在线程间的共享数据(例如堆内存)
②存在多个线程同时操作共享数据

2,锁类型
互斥锁:(重量级锁)同一时刻有且仅有一个线程操作共享数据,当某个线程正在操作共享数据时,其他线程处于等待状态,必须等到该线程处理完后再进行.
偏向锁:java1.6的新锁,针对多个线程竞争较少的场景,如果一个线程获得了锁,那么锁就进入偏向模式,对象头Mark Word里面的锁结构也编程偏向锁结构,这个线程再次请求锁时,无需做任何操作。
轻量级锁:依据是大部分的锁,整个同步周期内不存在竞争,适用线程交替执行同步块的场景。
自旋锁:依据是大部分线程持有锁的时间不会太长,因此自旋锁假设很快当前线程就会获得锁,所以虚拟机会让想要获取锁的线程执行几个空循环,如果循环完成后得到锁,则顺利进入。否则就会将线程在操作系统层面挂起。
锁消除:指虚拟机在编译时消除无用的锁。

3,synchronized关键字作用:
①保证同一时刻只有一个线程可以执行某个方法或者代码块
②可以保证一个线程操作的共享数据变化能立即被其他线程所见(可替代volatile)

4,synchronized关键字用法:
①修饰实例方法:作用于当前实例的方法(不包括静态方法),进入同步代码前要获取当前实例的锁(JVM通过常量池中的方法结构表的acc_synchronized标识区分一个方法是否是同步方法。)
public class SynchronizedClass extends Thread
{

static int share = 0;
//①修饰实例方法
public synchronized void increase()
{
    share++;
}

//②修饰静态方法
public synchronized static void increase4ClassLock(){
    share++;
}
public void run()
{
    for (int i = 0; i < 1000; i++)
    {
        increase();
    }
}

/**
 * @param args
 */
public static void main(String[] args)
{
    try
    {
        //场景1,t1,t2线程执行需要获得实例sc1的互斥锁
        SynchronizedClass sc1 = new SynchronizedClass();
        Thread t1 = new Thread(sc1);
        Thread t2 = new Thread(sc1);
        t1.start();
        t2.start();
        // join()方法:当前线程结束之后join()方法才返回,如果不试用join,打印出来的数据可能是0,1000,2000
        t1.join();
        t2.join();
        System.out.println("scene1 share:" + share);
        
        //share = 0;
        //场景2,t3,t4线程执行各自获得实例sc2,sc3的锁,可能同时访问共享的静态资源share出现线程安全问题
        SynchronizedClass sc2 = new SynchronizedClass();
        SynchronizedClass sc3 = new SynchronizedClass();
        Thread t3 = new Thread(sc2);
        Thread t4 = new Thread(sc3);
        t3.start();
        t4.start();
        t3.join();
        t4.join();
        System.out.println("scene2 share:" + share);
    } catch (Exception e)
    {
        e.printStackTrace();
    }
}

}
上面例子中:当前线程的锁是实例对象instance,java的线程同步锁可以是任意对象。注意:如果一个线程正在访问一个实例instance的synchronized方法A时,其他线程不能访问该实例的其他synchronized方法,因为一个对象实例只有一把锁,但其他线程可访问该实例的非synchronized方法。当然,如果其他线程要访问另外一个实例instance1的任何方法都是不受限制的,但是如果他们访问的是共享数据那么就会出问题,例如上面例子场景2中的静态变量share,这时候就需要使用②进行修饰。
②修饰静态方法:作用于当前类对象,进入同步代码前要获得当前类(class)对象的锁。注意:两个线程可以同时分别访问static synchronized修饰的方法和non-static synchronized修饰的方法,因为他们一个是类对象的锁,一个是实例对象的锁,但是此时两个方法如果同时操作共享数据,也可能出现线程安全问题。例如上面例子中两个线程分别同时访问:increase4ClassLock()和increase()方法,就会出现线程安全问题。
③修饰代码块:指定加锁对象,给指定对象家锁,进入同步代码块前要获取指定对象的锁(可以是类对象,也可以是实例对象),样例如下:
static SynchronizedClass inst1 = new SynchronizedClass();
public void increase2MethodBlock1(){

//使用实例锁,获取当前对象实例的锁,inst1也可用this(synchronized (inst1))
synchronized (inst1)
{
    share++;
}

}
public void increase2MethodBlock2(){

//使用类对象锁,获取当前class对象的锁
synchronized (SynchronizedClass.class)
{
    share++;
}

}

5,sychronized实现原理
jvm中synchronized的同步是通过管程(Monitor)实现的,在java的堆内存中,一个对象实例信息是按照如下方式存储的:

image

如上如所示结构,java对象头里面有锁信息,锁信息里有一个指向monitor(管程)对象的指针,管程对象在java虚拟机中是使用OjbectMonitor类实现的,类结构大致如下:
ObjectMonitor(){
_count = 0; //计数器
_WaitSet = null;
_EntryList = null;
_owner = null; //指向拥有ObjectMonitor对象的线程
……
}
说明:
①多个线程进入同步代码时,会先进入_ EntryList队列,获取到monitor对象后修改owner指向为当前对象线程,计数器加1
②线程调用wait()方法进入_WaitSet队列,owner置为空,count减1
③当前线程执行完毕也会重置owner为空,复位变量
image

6,synchronized需要注意的点
①可重入性
一个线程在synchronized方法体内部可以调用另外一个synchronized方法,因为是同一个线程请求同一个锁。子类继承父类时,子类可通过重入锁调用父类的synchronized方法。
②在线程sleep时调用interrupt方法会打断阻塞并抛出异常(对阻塞线程进行中断操作):
image

③处于非阻塞状态的线程需要我们手动进行中断检测并结束程序:
image

如上②和③两种情况(阻塞/和线程运行时中断中断的操作方法是不一样的[前者要捕捉异常,后者要手动判断中断状态并做中断处理]),可用下面代码兼顾两种场景:
image

④线程状态转换
image
1)等待阻塞:等待阻塞(使用wait,notify/notifyAll方法进入和退出)(wait是Object类的方法)
2)锁定阻塞:synchronized同步锁的获得和释放
3)其他阻塞:sleep,join,IO操作等(sleep是Thread类的静态方法)
4)就绪状态获得时间片就进入运行态,时间片用完或yield操作回到就绪状态
5)sleep和wait的区别:sleep方法是Thread类的今天方法而wait是Object类的方法;sleep方法不会释放对象锁,sleep时其他线程任然不能访问同步块,而wait方法会释放对象锁。
image
⑤线程中断操作对正在等待获取锁的synchronized方法或者代码块是不起作用的:一个正在等待锁的线程要么获得锁继续执行,要么继续等待。
image

⑥wait(); notify(); notifyAll();方法必须放在synchronized中,否则会抛出IllegalMonitorStateException异常,因为:调用这几个方法必须获取管程(ObjectMonitor)对象,该对象放在对象头中,而synchronized可以获取到该对象。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SAP成都研究院CEC团队三巨头之一:M君的文章预告
国人总倾向于把特点或者作用类似的人或物放在一起比较并做出排名,于是就有了许多“某某某三巨头”的称谓。
3 0
如何将S/4HANA系统存储的图片文件用Java程序保存到本地
我在S/4HANA的事务码MM02里为Material维护图片文件作为附件:
4 0
SpringCloud-分布式配置中心【加密-非对称加密】
非对称加密我们需要生成对应的公钥和私钥,jdk中提供的有java-keytool工具帮助我们生成
4 0
ECS使用体验
阿里云开发者社区里不光有着丰富的知识,还有着十分优秀的性能和非常优秀的服务!他的优点还很多,对于我来说他的优点,是可以花费很少的钱去学习,性价比高,里面的知识丰富,我很需要。 还有的就是,我使用阿里云,感觉非常省力,不需要花时间担心卡掉或死机的问题。
3 0
ECS体验
阿里云使用起来,非常省力。不需要花时间维修服务器硬件,有阿里监控服务器,让我很放心。 阿里云服务器稳定,不会出现异常关机等掉线状况。 阿里云服务器非常安全,不会出现安全问题。 阿里云的价格也非常的亲民,非常省钱,尤其对我们学生用户非常友好。
4 0
JPA + EclipseLink + SAP云平台 = 运行在云端的数据库应用
JPA(Java Persistence API)的实现Provider有Hibernate,OpenJPA和EclipseLink等等。 本文介绍如何通过JPA + Eclipse连接SAP云平台上的HANA数据库实例。 1. 登陆SAP云平台Cockpit,创建一个新的HANA DB实例:
4 0
使用ABAP(ADBC)和Java(JDBC)连接SAP HANA数据库
在表DBCON里维护一条记录,指向HANA数据库。con_ENV里填入HANA数据库的主机名和端口号。如vmXXXX:30015
4 0
码出高效:Java开发手册-第2章(7)
本章开始讲解面向对象思想,并以Java 为载体讲述面向对象思想在具体编程语言中的运用与实践。当前主流的编程语言有50 种左右,主要分为两大阵营:面向对象编程与面向过程编程。面向对象编程(Object-Oriented Programming,OOP)是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。OOP 的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木一样隔离可控、高效简单,是当今编程领域的一股势不可......
4 0
ECS使用感受
阿里云服务器初体验
4 0
+关注
9
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载