synchronize——对象锁和类锁
最近在研究Java 多线程的只是,经常能看到synchronize关键字,以前只是一眼带过,没有细究,今天趁这个机会,整理下
synchronize作为多线程关键字,是一种同步锁,它可以修饰以下几种对象:
代码块:被修饰的代码块称为同步语句块,其作用的范围是大括号{ }里的代码,作用的对象是调用这个代码块的对象;
方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
静态方法:作用的范围是整个静态方法,作用的对象是这个类的所有对象
类:作用的范围是synchronize后面括号里的部分,作用的对象是这个类的所有对象
一、synchronize关键字
1.修饰方法
synchronized public void getValue() {
System.out.println("getValue method thread name="
+ Thread.currentThread().getName() + " username=" + username
+ " password=" + password);
}
2.修饰代码块
public void serviceMethod() {
try {
synchronized (this) {
System.out.println("begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end end=" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
3.修饰类
public static void printA() {
synchronized (Service.class) {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "离开printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
二、Java中的对象锁和类锁
借用网友对对象锁和类锁定义
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,
在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁);
如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。
取到锁后,他就开始执行同步代码(被synchronized修饰的代码);
线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。
这样就保证了同步代码在统一时刻只有一个线程在执行。
三、synchronize的用法与实例
总结:
synchronize修饰非静态方法、同步代码块的synchronize(this)和synchronize(非this对象)的用法锁的是对象,线程想要执行对应的同步代码,需要获得对象锁。
synchronize修饰静态方法以及同步代码块的synchronize(类.class)用法锁是类,线程想要执行对应的同步代码,需要获得类锁。
1.首先看下非线程安全例子
public class Run { public static void main(String[] args) { HasSelfPrivateNum numRef = new HasSelfPrivateNum(); ThreadA athread = new ThreadA(numRef);
athread.start(); ThreadB bthread = new ThreadB(numRef);
bthread.start(); } } class HasSelfPrivateNum { private int num = 0; public void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
} @Override
public void run() {
super.run();
numRef.addI("a");
} } class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
} @Override
public void run() {
super.run();
numRef.addI("b");
} }
运行结果:
a set over!
b set over!
b num=200
a num=200
修改HasSelfPrivateNum如下,方法用synchronize修饰如下:
class HasSelfPrivateNum { private int num = 0; synchronized public void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
运行结果是线程安全的:
b set over!
b num=200
a set over!
a num=100
由于sleep方法不会放弃对象锁,需要等到时间结束,释放锁,下一个进程才能获取到该对象锁
实验结论:两个线程访问同一个对象中的同步方法是一定是线程安全的。本实现由于是同步访问,所以先打印出a,然后打印出b。
这里线程获取的是HasSelfPrivateNum的对象实例的锁——对象锁。
2.多个对象多个锁
public class Run { public static void main(String[] args) { HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
HasSelfPrivateNum numRef2 = new HasSelfPrivateNum(); ThreadA athread = new ThreadA(numRef1);
athread.start(); ThreadB bthread = new ThreadB(numRef2);
bthread.start(); } }
运行结果:
a set over!
b set over!
b num=200
a num=200
这里是非同步的,因为线程athread获得是numRef1的对象锁,而bthread线程获取的是numRef2的对象锁,他们并没有在获取锁上有竞争关系,因此,出现非同步的结果。
3.同步块synchronize(this)
public class Run { public static void main(String[] args) {
ObjectService service = new ObjectService(); ThreadA a = new ThreadA(service);
a.setName("a");
a.start(); ThreadB b = new ThreadB(service);
b.setName("b");
b.start();
} } class ObjectService { public void serviceMethod() {
try {
synchronized (this) {
System.out.println("begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end end=" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class ThreadA extends Thread { private ObjectService service; public ThreadA(ObjectService service) {
super();
this.service = service;
} @Override
public void run() {
super.run();
service.serviceMethod();
} } class ThreadB extends Thread {
private ObjectService service; public ThreadB(ObjectService service) {
super();
this.service = service;
} @Override
public void run() {
super.run();
service.serviceMethod();
}
}
运行结果:
begin time=1466148260341
end end=1466148262342
begin time=1466148262342
end end=1466148264378
这样也是同步的,线程获取的是同步块synchronized (this)括号()里面的对象实例的对象锁,这里就是ObjectService实例对象的对象锁了。
4.synchronize(非this对象)
public class Run { public static void main(String[] args) { Service service = new Service("xiaobaoge"); ThreadA a = new ThreadA(service);
a.setName("A");
a.start(); ThreadB b = new ThreadB(service);
b.setName("B");
b.start(); } } class Service { String anyString = new String(); public Service(String anyString){
this.anyString = anyString;
} public void setUsernamePassword(String username, String password) {
try {
synchronized (anyString) {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "进入同步块");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "离开同步块");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class ThreadA extends Thread {
private Service service; public ThreadA(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.setUsernamePassword("a", "aa"); } } class ThreadB extends Thread { private Service service; public ThreadB(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.setUsernamePassword("b", "bb"); } }
不难看出,这里线程争夺的是anyString的对象锁,两个线程有竞争同一对象锁的关系,出现同步。
5.静态synchronize同步方法
public class Run { public static void main(String[] args) { ThreadA a = new ThreadA();
a.setName("A");
a.start(); ThreadB b = new ThreadB();
b.setName("B");
b.start(); } } class Service { synchronized public static void printA() {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "离开printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
} synchronized public static void printB() {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
+ System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
+ System.currentTimeMillis() + "离开printB");
} } class ThreadA extends Thread {
@Override
public void run() {
Service.printA();
} } class ThreadB extends Thread {
@Override
public void run() {
Service.printB();
}
}
运行结果:
线程名称为:A在1466149372909进入printA
线程名称为:A在1466149375920离开printA
线程名称为:B在1466149375920进入printB
线程名称为:B在1466149375920离开printB
两个线程在争夺同一个类锁,因此同步。
6.synchronize(class)
class Service { public static void printA() {
synchronized (Service.class) {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "离开printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
} } public static void printB() {
synchronized (Service.class) {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ "在" + System.currentTimeMillis() + "离开printB");
}
}
}
运行结果:
线程名称为:A在1466149372909进入printA
线程名称为:A在1466149375920离开printA
线程名称为:B在1466149375920进入printB
线程名称为:B在1466149375920离开printB
两个线程依旧在争夺同一个类锁,因此同步。
总结:
A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制
synchronize——对象锁和类锁的更多相关文章
- Java锁Synchronized,对象锁和类锁举例
Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...
- Java锁Synchronized对象锁和类锁区别
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...
- Java对象锁和类锁全面解析(多线程synchronized关键字)
最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不过是别人怎么用就跟着用,并没有搞清楚锁的概念.最近也是遇到一些问题,不搞清楚锁的概念,很容易碰壁,甚至有些时候自己连用没 ...
- java的同步方法和同步代码块,对象锁,类锁区别
/** * @author admin * @date 2018/1/12 9:48 * 作用在同一个实例对象上讨论 * synchronized同步方法的测试 * 两个线程,一个线程调用synchr ...
- Synchronized方法锁、对象锁、类锁区别
synchronized,这个东西我们一般称之为”同步锁“,他在修饰代码块的时候需要传入一个引用对象作为“锁”的对象. 在修饰方法的时候,默认是当前对象作为锁的对象 在修饰类时,默认是当前类的Clas ...
- java的对象锁和类锁
在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法. 因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识. j ...
- java 对象锁和类锁的区别(转)
java 对象锁和类锁的区别 转自; ) ); ; ) ); 上述的代码,第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象,当然,如果需要同步其他对象实例,也不可 ...
- 【Thread】java线程之对象锁、类锁、线程安全
说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来 ...
- java线程同步以及对象锁和类锁解析(多线程synchronized关键字)
一.关于线程安全 1.是什么决定的线程安全问题? 线程安全问题基本是由全局变量及静态变量引起的. 若每个线程中对全局变量.静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的:若有多个线 ...
随机推荐
- Java生成艺术二维码也可以很简单
原文点击: Quick-Media Java生成艺术二维码也可以很简单 现在二维码可以说非常常见了,当然我们见得多的一般是白底黑块,有的再中间加一个 logo,或者将二维码嵌在一张特定的背景中(比如微 ...
- 【转帖】HBase基本概念与基本使用
HBase基本概念与基本使用 https://www.cnblogs.com/swordfall/p/8737328.html 分类: HBase undefined 1. HBase简介 1.1 什 ...
- 用Qt实现一个计算器
一· 介绍 目的: 做一个标准型的计算器.用于学习Qt基础学习. 平台: Qt 5.12.0 二· 结构框架设计 2.1最终产品样式 界面的设计大体按照win系统自带的计算器做模仿.左边是win7 的 ...
- 如何同时读取 TDateTimePicker 的 Date 和 Time ?
由于 TDateTimePicker 只能用于日期或时间,不能同时使用.如果将Kind属性设置为dtkDate,则可以指定自定义DATE格式,但忽略任何TIME格式,并且Time未定义使用该属性.如果 ...
- Mysql的常见索引
PRIMARY KEY(主键索引) ALTER TABLE table_name ADD PRIMARY KEY ( col ) 它是一种特殊的唯一索引,不允许有空值: UNIQUE(唯一索引) AL ...
- sql特殊日期
--a. 本月的第一天 select dateadd(mm, datediff(mm,0,getdate()), 0) as 本月的第一天 --b. 本月的最后一天 select dateadd(ms ...
- Fiddler抓包8-打断点(bpu)(转)
转自:https://www.cnblogs.com/yoyoketang/p/6778006.html
- MOOC C#笔记(一):数据类型
C#笔记 基础知识 一个C#程序主要包括以下部分: 命名空间声明(Namespace declaration) 一个 class Class 方法 Class 属性 一个 Main 方法 语句(Sta ...
- java之mybatis之动态sql
1. if 判读条件是否满足,满足将会把 sql 语句加上. <select id="findUser" parameterType="Map" resu ...
- Hadoop—MapReduce计算气象温度
Hadoop-MapReduce计算气象温度 1 运行环境说明 1.1 硬软件环境 主机操作系统:Mac OS 64 bit ,8G内存 虚拟软件:Parallers Desktop12 虚拟机操作系 ...