多线程简介

在现代计算机中往往存在多个CPU核心,而1CPU能同时运行一个线程,为了充分利用CPU多核心,提高CPU的效率,多线程就应时而生了。

那么多线程就一定比单线程快吗?答案是不一定,因为多线程存在单线程没有的问题

  • 上下文切换:线程从运行状态切换到阻塞状态或者等待状态的时候需要将线程的运行状态保存,线程从阻塞状态或者等待状态切换到运行状态的时候需要加载线程上次运行的状态。线程的运行状态从保存到再加载就是一次上下文切换,而上下文切换的开销是非常大的,而我们知道CPU给每个线程分配的时间片很短,通常是几十毫秒(ms),那么线程的切换就会很频繁。
  • 死锁:死锁的一般场景是,线程A和线程B都在互相等待对方释放锁,死锁会造成系统不可用。
  • 资源限制的挑战:资源限制指计算机硬件资源或软件资源限制了多线程的运行速度,例如某个资源的下载速度是1Mb/s,资源的服务器带宽只有2Mb/s,那么开10个线程下载资源并不会将下载速度提升到10Mb/s

既然多线程存在这些问题,那么我们在开发的过程中有必要使用多线程吗?我们知道任何技术都有它存在的理由,总而言之就是多线程利大于弊,只要我们合理使用多线程就能达到事半功倍的效果。

多线程的意思就是多个线程同时工作,那么多线程之间如何协同合作,这也就是我们需要解决的线程通信线程同步问题

  • 线程通信:线程通信指线程之间以何种机制来交换消息,线程之间的通信机制有两种:共享内存消息传递。共享内存即线程通过对共享变量的读写而达到隐式通信,消息传递即线程通过发送消息给对方显示的进行通信。
  • 线程同步:线程同步指不同线程对同一个资源进行操作时候线程应该以什么顺序去操作,线程同步依赖于线程通信,以共享内存方式进行线程通信的线程同步是显式的,以消息传递方式进行线程通信的线程同步是隐式的。

synchronized简介

synchronized是Java的关键字,可用于同步实例方法、类方法(静态方法)、代码块

  • 同步实例方法:当synchronized修饰实例方法的时候,同步的范围是当前实例的实例方法。
  • 同步类方法:当synchronized修饰类方法的时候,同步的范围是当前类的方法。
  • 同步代码块:当synchronized修饰代码块的时候,同步的范围是()中的对象。

"talk is cheap show me the code"让我们分别运行个例子来看看。

  1. 同步实例方法
synchronized public void synSay() {System.out.println("synSay----" + Thread.currentThread().getName());while (true) { //保证进入该方法的线程 一直占用着该同步方法}
}public void say() {System.out.println("say----" + Thread.currentThread().getName());
}
public static void main(String[] args){Test test1 = new Test();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {test1.synSay();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);  //休眠3秒钟 保证线程t1先执行} catch (InterruptedException e) {e.printStackTrace();}test1.say();test1.synSay();}});t1.start();t2.start();
}
复制代码

运行输出

synSay----Thread-0  //线程t1
say----Thread-1  //线程t2
复制代码

创建t1t2两个线程,分别执行同一个实例test1的方法,线程t1先执行加了同步关键字的synSay方法,注意方法里面需要加上个while死循环,目的是让线程一直在同步方法里面,然后然线程t1执行之后再让线程t2去执行,此时线程t2并不能成功进入到synSay方法里面,因为此时线程t1正在方法里面,线程2只能在synSay方法外面阻塞,但是线程t2可以进入到没有加同步关键字的say方法。
也就是说关键字synchronized修饰实例方法的时候,锁住的是该实例的加了同步关键字的方法,而没有加同步关键字的方法,线程还是可以正常访问的。但是不同实例之间同步是不会影响的,因为每个实例都有自己的一个锁,不同实例之间的锁是不一样的。

  1. 同步类方法
synchronized static public void synSay() {System.out.println("static synSay----" + Thread.currentThread().getName());while (true) { //保证进入该方法的线程 一直占用着该同步方法}
}synchronized public void synSay1() {System.out.println("synSay1----" + Thread.currentThread().getName());
}public void say() {System.out.println("say----" + Thread.currentThread().getName());
}
public static void main(String[] args){Test test1 = new Test();Test test2 = new Test();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {test1.synSay();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);  //休眠3秒钟 保证线程t1先执行} catch (InterruptedException e) {e.printStackTrace();}test1.say();test2.say();test1.synSay();}});t1.start();t2.start();
}
复制代码

运行输出

static synSay----Thread-0 //线程t1 实例test1
say----Thread-1 //线程t2 实例test1
say----Thread-1 //线程t2 实例test2static synSay----Thread-0 //线程t1 实例test1
say----Thread-1  //线程t2 实例test1
synSay1----Thread-1 //线程t2 实例test1
say----Thread-1 //线程t2 实例test2
复制代码

这里和上面的同步实例方法的代码差不多,就是将synSay方法加上了static修饰符,即把方法从实例方法变成类方法了,然后我们再新建个实例test2,先让线程t1调用实例test1的synSay类方法,在让线程t2去调用实例test1的say实例方法、synSay类方法和让线程t2去调用实例test2的say实例方法,发现在线程t1占用加了同步关键字的synSay类方法的时候,别的线程是不能调用加了锁的类方法的,但是可以调用没有加同步关键字的方法或者加了同步关键字的实例方法,也就是说每个类有且仅有11个锁,每个实例有且仅有1个锁,但是每个类可以有一个或者多个实例,类的锁和实例的锁不会相互影响,实例之间的锁也不会相互影响。需要注意的是,一个类和一个实例有且仅有一个锁,当这个锁被其他线程占用了,那么别的线程就无法获得锁,只有阻塞等待

  1. 同步代码块
    public void synSay() {String x = "";System.out.println("come in synSay----" + Thread.currentThread().getName());synchronized (x) {System.out.println("come in synchronized----" + Thread.currentThread().getName());while (true) { //保证进入该方法的线程 一直占用着该同步方法}}}
public static void main(String[] args){Test test1 = new Test();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {test1.synSay();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);  //休眠3秒钟 保证线程t1先执行} catch (InterruptedException e) {e.printStackTrace();}test1.synSay();}});t1.start();t2.start();
}
复制代码

运行输出

come in synSay----Thread-0
come in synchronized----Thread-0
come in synSay----Thread-1
复制代码

可以发现同步代码块和同步实例方法、同步类方法其实差不多,但是同步代码块将同步的范围缩小了,可以同步到指定的对象上,而不像同步实例方法、同步类方法那样同步的是整个方法,所以同步代码块在效率上比其他两者都有较大的提升。
需要注意的是,当同步代码块的时候,在类方法中加入同步代码块且同步的对象是xx.class等类的引用的时候,同步的是该类,如果在****实例方法中加入同步代码块且同步的对象是this,那么同步的是该实例,可以看成前者使用的是类的锁**,后者使用的是实例的锁

synchronized的特性

建议把volatile的特性和synchronized的特性进行对比学习,加深理解。《Java volatile关键字解析》

synchronized与可见性

JMM关于synchronized的两条语义规定了:

  • 线程加锁前:需要将工作内存清空,从而保证了工作区的变量副本都是从主存中获取的最新值。
  • 线程解锁前;需要将工作内存的变量副本写回到主存中。

大概流程:清空线程的工作内存->在主存中拷贝变量副本到工作内存->执行完毕->将变量副本写回到主存中->释放锁
所以synchronized能保证共享变量的可见性,而实现这个流程的原理也是通过插入内存屏障,和关键字volatile相似。

synchronized与有序性

因为synchronized是给共享变量加锁,即使用阻塞的同步机制,共享变量只能同时被一个线程操作,所以JMM不用像volatile那样考虑加内存屏障去保证synchronized多线程情况下的有序性,因为CPU在单线程情况下是保证了有序性的
所以synchronized修饰的代码,是保证了有序性的。

synchronized与原子性

同样因为synchronized是给共享变量加锁了,以阻塞的机制去同步,在对共享变量进行读/写操作的时候是原子性的。
所以synchronized修饰的代码,是能保证原子性的。

参考

Java并发编程的艺术
内存可见性和原子性:Synchronized和Volatile的比较
java synchronized类锁,对象锁详解(转载)

原文地址:ddnd.cn/2019/03/21/…

转载于:https://juejin.im/post/5c91baf6e51d45256822ec5e

synchronized的使用(一)相关推荐

  1. 【java线程】锁机制:synchronized、Lock、Condition

    [Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...

  2. java static 可见性_Java多线程 synchronized与可见性的关系以及可见性问题总结

    作者:七里香的编程之路 出自:OSCHINA 原文:my.oschina.net/u/4098550/blog/4548274 能保证可见性的措施 除了volatile 可以让变量保证可见性外.hap ...

  3. 你真的掌握了并发编程volatile synchronized么?

    先看代码: import java.util.concurrent.atomic.AtomicInteger;/**** @author xialuomantian*/ public class Ne ...

  4. Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现

    关于怎么查看字节码的五种方法参考本人另一篇文章<Java以及IDEA下查看字节码的五种方法> 查看汇编语言汇编码 说要看汇编还是很有必要的,因为有些地方比如加锁其实还是通过汇编实现的,只看 ...

  5. java并发vol_java 并发中 volitile、synchronized和lock的比较(一)

    1.volitile和(synchronnized.lock) 首先比较volitile和synchronnized,volitile线程不安全,但是synchronized则是线程安全的. voli ...

  6. synchronized底层原理_你用过synchronized吗?它的底层原理是什么?Java经典面试题来了...

    并发编程已经成为程序员必备技能 作为Java程序员,不懂得并发编程显然已经不能满足市场需求了,尤其是在面试过程中将处于被动地位,也有可能面试将就此终结. 那么作为Java开发者的你,日常虽然可以基于J ...

  7. 面试题-自旋锁,以及jvm对synchronized的优化

    背景 想要弄清楚这些问题,需要弄清楚其他的很多问题. 比如,对象,而对象本身又可以延伸出很多其他的问题. 我们平时不过只是在使用对象而已,怎么使用?就是new 对象.这只是语法层面的使用,相当于会了一 ...

  8. JAVA多线程之Synchronized、wait、notify实例讲解

    一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...

  9. ReentrantLock与synchronized

    1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的 ...

  10. Java并发之synchronized

    synchronized关键字最主要有以下3种应用方式 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁:实例锁,一个实例一把锁 修饰静态方法,作用于当前类对象加锁,进入同步代码前要 ...

最新文章

  1. ubuntu12.0.4不能正常关机和重启的解决方法
  2. .NET中栈和堆的比较(二)
  3. python实现kmean算法_K-means聚类算法的Python实现,Kmeans
  4. 文本分类--情感分析
  5. python执行shell命令、并获取执行过程信息_python执行使用shell命令方法
  6. STL--map用法
  7. JDK1.8与spring3.x的不兼容
  8. Google App Engine CMS系统的搭建
  9. Aiml中文包含英文(字母,特殊符号)识别问题的解决
  10. redis击穿、redis雪崩、redis穿透
  11. 汉诺塔解法C语言实现
  12. 沉没成本谬误(一):你是如何被“套牢”的?(二):吃饱撑着也要把东西吃完
  13. 精力充沛才能走得更远更从容
  14. 蓝桥杯算法--计算纪念日
  15. 全年涨幅超200%,来自东南亚的Sea Limited有何魔力?
  16. html背景图片的隐藏,CSS隐藏图片背景上方的文字内容
  17. 研发人员的特质_9个有效开发人员的人格特质
  18. 写给自己过去的一封信:成功=好习惯+自控力
  19. 南京邮电大学高级语言程序设计实验五(指针与字符串实验)
  20. 常用计算机的储存设备有哪些,【好物榜单】常用数据存储设备推荐

热门文章

  1. JavaScript中一些常用的方法整理
  2. HDU3549+Ford-Fulkerson
  3. 排查MySQL同步延迟思路
  4. Linux运维课程 第一阶段 重难点摘要(一)网络基础
  5. ubuntu系统颜色更改
  6. C++ Primer Plus 读书笔记(第4、5章)
  7. DAO基本登录(1)
  8. 两篇介绍IIS的文章
  9. asp.net夜话之十一:web.config详解
  10. Matlab关联m文件与m文件关联设置