前言


多线程是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. 面试题: Spring 框架 Bean的生命周期

    [Java面试五]Spring总结以及在面试中的一些问题.   1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本 ...

  2. Entity Framework Code-First(13):Configure Many-to-Many

    Configure Many-to-Many relationship: Here, we will learn how to configure Many-to-Many relationship ...

  3. ascII、iso、utf-8、Unicode的区别

    utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念,utf-8是unicode的实现方式. 要想先讲清楚他们的区别,首先 ...

  4. rest framework 权限

    一.权限示例 需求:不同的用户类型有不同的权限 普通用户:只能查看个人信息相关,序号:1 VIP 用户:只能查看个人信息相关,序号:2 SVIP 用户:查看订单相关信息,序号:3 1.新建 app/u ...

  5. C# 原码与补码的转换

    /// <summary> /// 求一个16位数数的补码 /// </summary> /// <param name="OriginalCode" ...

  6. Git fatal:$'GIT_DIR' too big

    这句表示的是创建的文件的名称太长了,将你需要放置的代码的文件名字改短一些即可!

  7. OpenStack基础知识-项目打包的步骤

    学习过包管理相关的知识后,我们就要以OpenStack的方法来创建一个我们自己的项目.这个项目的名称是webdemo,就是一个简单的web服务器.这个项目会贯穿这个系列文章.在本文中,我们首先要创建w ...

  8. PHP 各个版本的区别

    查看详细内容

  9. 洛谷P1065 作业调度方案

    P1065 作业调度方案 题目描述 我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成.每个工件的每道工序都有指定的加工时间. 每个工件的每个工序称为一个操作 ...

  10. bzoj4514: [Sdoi2016]数字配对(费用流)

    传送门 ps:费用流增广的时候费用和流量打反了……调了一个多小时 每个数只能参与一次配对,那么这就是一个匹配嘛 我们先把每个数分解质因数,记质因子总个数为$cnt_i$,那如果$a_i/a_j$是质数 ...