前言


多线程是Java面试中最喜欢问的问题之一,有一篇公众号推文内容很详细,推荐看看

但凡面试官考“多线程”,一定会问这10个问题!

本文中仅对synchronized关键字的加锁进行一定分析

一、标准情况访问


按照普通的情况访问同步方法,查看输出

 class Phone {
public synchronized void getIOS() throws Exception {
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
} public class ThreadDemo { public static void main(String[] args) {
final Phone phone = new Phone();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:

---getIOS

---getAndroid

二、在其中一种方法中添加sleep方法访问


如下代码,在getIOS方法中添加sleep,查看修改后的打印顺序

class Phone {
public synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
}

执行结果:(两个结果在3秒后同时出现)

---getIOS

---getAndroid

三、添加一个未加锁方法,查看访问结果


在Phone类中添加getHello方法,同时线程修改为访问同步方法和普通方法

class Phone {
public synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) {
final Phone phone = new Phone();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
//phone.getAndroid();
phone.getHello();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:

---getHello(直接出现)

---getIOS(三秒后出现)

四、执行时创建两个不同对象,通过不同对象访问加锁的方法


在创建线程时,通过不同对象执行同步方法,查看执行结果

class Phone {
public synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone();
final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
//phone.getAndroid();
//phone.getHello();
phone2.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:

---getAndroid(直接出现)

---getIOS(三秒后出现)

五、将加锁的方法改为静态方法,同一个对象执行


两个同步方法都改为静态方法,通过同一个对象执行方法,查看执行结果

class Phone {
public static synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public static synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone();
//final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getAndroid();
//phone.getHello();
//phone2.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:(两个结果在三秒后同时出现)

---getIOS

---getAndroid

六、通过两个对象访问静态同步方法


将上一个的执行对象改为phone2,查看执行结果

class Phone {
public static synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public static synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone();
final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
//phone.getAndroid();
//phone.getHello();
phone2.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:(两个结果在三秒后同时出现)

---getIOS

---getAndroid

七、一个静态同步方法修改为非静态,通过同一个对象执行


其中的一个方法去掉静态关键字,查看执行结果

class Phone {
public static synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone();
final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getAndroid();
//phone.getHello();
//phone2.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:

---getAndroid(直接出现)

---getIOS(三秒后出现)

八、一个静态同步方法修改为非静态,通过两个对象执行


其中的一个方法去掉静态关键字,查看执行结果

class Phone {
public static synchronized void getIOS() throws Exception {
Thread.sleep(3000);
System.out.println("---getIOS");
}
public synchronized void getAndroid() throws Exception {
System.out.println("---getAndroid");
}
public void getHello() {
System.out.println("---getHello");
}
} public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone();
final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
phone.getIOS();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 1; i++) {
try {
//phone.getAndroid();
//phone.getHello();
phone2.getAndroid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "BB").start();
} }

执行结果:

---getAndroid(直接出现)

---getIOS(三秒后出现)

总结


1. 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法

2. 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

3. 加个普通方法后发现和同步锁无关

4. 换成两个对象后,不是同一把锁了,情况立刻变化。

5. 都换成静态同步方法后,情况又变化

所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。

所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

synchronized锁——8锁的更多相关文章

  1. synchronized内置锁

    synchronized内置锁,如果发生阻塞,无法被中断,除非关闭jvm.因此不能从死锁中恢复.

  2. synchronized关键字以及实例锁 类锁

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...

  3. (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别

    场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...

  4. Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

    (1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...

  5. Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)

    不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...

  6. 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字,在java同步代码快中,synchronized的使用方式无非有两个: 通过对一个对象进行加锁来实现同步,如下面代码. synchr ...

  7. Synchronized 和 Lock 锁在JVM中的实现原理以及代码解析

    一.深入JVM锁机制:synchronized synrhronized关键字简洁.清晰.语义明确,因此即使有了Lock接口,使用的还是非常广泛.其应用层的语义是可以把任何一个非null对象作为&qu ...

  8. synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化

    进入时:monitorenter 每个对象有一个监视器锁(monitor).当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1 ...

  9. 【转载】Java中的锁机制 synchronized & 偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景 & AtomicReference

    参考文章: http://blog.csdn.net/chen77716/article/details/6618779 目前在Java中存在两种锁机制:synchronized和Lock,Lock接 ...

  10. Java多线程简析——Synchronized(同步锁)、Lock以及线程池

    Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...

随机推荐

  1. css之布局

    布局一直是页面制作很重要的部分,有个良好的布局不仅在页面上呈现很好的效果,还对后续功能扩展有重要的作用.本文主要讨论一下几种布局: 水平居中布局 垂直居中布局 多列布局 自适应布局 stracky-f ...

  2. day03 hadoop的解压与配置文件的配置,还需要看视频

    拷贝hadoop1.2.1.tar.gz安装包到其他的节点上 scp -r ~/hadoop-1.2.1.tar.gz  root@node2:~/  tar -zxvf hadoop-1.2.1.t ...

  3. 一个自动修改本地IP地址的BAT

    set /a num1=%random%%%200+1+1  //生成随机数set ip=192.168.1.//ip 主体set ip1=%ip%%num1% //拼接两部分cmd /c netsh ...

  4. MySql获取记录的名次

    在oracle中有rownum之类的东西表示记录的名次,那么在MySql中怎么获取名次呢? as rank ) B 获取的rank就是名次了 user_id rank 134762    122139 ...

  5. UCSC数据库数据调用cruzdb

    https://github.com/Wy2160640/cruzdb UCSC基因组数据库是注释,调节和变异以及越来越多的分类群的各种数据的重要资源. 该库旨在简化数据的利用,以便我们可以进行复杂的 ...

  6. p2055&bzoj1433 假期的宿舍

    传送门(洛谷) 传送门(bzoj) 题目 学校放假了······有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如A 和B都是学校的学生,A要回家,而C来看B,C与A不认识. ...

  7. 子元素应该margin-top影响父元素的解决办法

    在子元素设置margin-top,有时会带着父元素一起移动. 原因: Outer Div [margin: 0 auto] Inner Div [margin-top: 10px] 根据CSS2.1盒 ...

  8. [转][译]ASP.NET MVC 4 移动特性

    此教程将讨论ASP.NET MVC 4 Web应用程序里的移动特性.对于此教程,可以使用 Visual Studio Express 2012 或者 Visual Web Developer 2010 ...

  9. [CentOS7] 设备与文件名对应表

  10. (linux)修改MySQL密码方法

    1,在/etc/my.cnf末尾  加入skip-grant-tables,保存,跳过身份验证. 2,重启MySql,使刚才修改的配置生效. 3,终端输入mysql,然后再输入use mysql; 4 ...