最近在研究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——对象锁和类锁的更多相关文章

  1. Java锁Synchronized,对象锁和类锁举例

    Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...

  2. Java锁Synchronized对象锁和类锁区别

    java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...

  3. Java对象锁和类锁全面解析(多线程synchronized关键字)

    最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不过是别人怎么用就跟着用,并没有搞清楚锁的概念.最近也是遇到一些问题,不搞清楚锁的概念,很容易碰壁,甚至有些时候自己连用没 ...

  4. java的同步方法和同步代码块,对象锁,类锁区别

    /** * @author admin * @date 2018/1/12 9:48 * 作用在同一个实例对象上讨论 * synchronized同步方法的测试 * 两个线程,一个线程调用synchr ...

  5. Synchronized方法锁、对象锁、类锁区别

    synchronized,这个东西我们一般称之为”同步锁“,他在修饰代码块的时候需要传入一个引用对象作为“锁”的对象. 在修饰方法的时候,默认是当前对象作为锁的对象 在修饰类时,默认是当前类的Clas ...

  6. java的对象锁和类锁

    在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法. 因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识. j ...

  7. java 对象锁和类锁的区别(转)

    java 对象锁和类锁的区别   转自; ) ); ; ) ); 上述的代码,第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象,当然,如果需要同步其他对象实例,也不可 ...

  8. 【Thread】java线程之对象锁、类锁、线程安全

    说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来 ...

  9. java线程同步以及对象锁和类锁解析(多线程synchronized关键字)

    一.关于线程安全 1.是什么决定的线程安全问题? 线程安全问题基本是由全局变量及静态变量引起的. 若每个线程中对全局变量.静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的:若有多个线 ...

随机推荐

  1. npm 创建vue项目(指定目录进行创建)

    1.先安装node,js和npm 检验mpm 和node的方式是 npm -v  / node -v 2.安装最新版本 npm install @vue/cli -g 意外安装老版本的是代码  npm ...

  2. 基于redis+lua实现高并发场景下的秒杀限流解决方案

    转自:https://blog.csdn.net/zzaric/article/details/80641786 应用场景如下: 公司内有多个业务系统,由于业务系统内有向用户发送消息的服务,所以通过统 ...

  3. Python3 - Bytes类型

    Python3 新增 bytes 类型,是指一堆字节的集合,十六进制表现形式,两个十六进制数构成一个 byte ,以 b 开头的字符串都是 bytes 类型. 计算机只能存储二进制,字符.图片.视频. ...

  4. qt5信息提示框QMessageBox用法(很全)

    information QMessageBox::information(NULL, "Title", "Content", QMessageBox::Yes ...

  5. 软件测试人员必备的Python知识图库

    UI自动化测试(Python+Selenium等) 接口测试(Python requests等) 性能测试(Python Locust等) 安全性测试(Python Scapy等) 兼容性测试(Pyt ...

  6. C# vb .net实现棕褐色效果特效滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的棕褐色效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一 ...

  7. 【转载】C#中List集合使用Clear方法清空集合

    在C#中的List集合操作过程中,有时候需要清空List集合中的元素对象,将之重置为一个初始化的List集合对象,此时就可以使用到List集合的扩展方法Clear()方法,此方法将清空List集合中所 ...

  8. vue中常见的指令

    1,差值表达式{{}} <p >{{ msg }}</p> 2.v-cloak解决差值表达式闪烁的问题 <p v-cloak>{{ msg }}</p> ...

  9. mvc和mvvm模式

    一. Mvvm定义 MVVM是Model-View-ViewModel的简写.即模型-视图-视图模型.[模型]指的是后端传递的数据.[视图]指的是所看到的页面.[视图模型]mvvm模式的核心,它是连接 ...

  10. Java的基本知识之线程池篇

    1.基本概念 1.共享资源 多个线程对同一份资源进行访问(读写操作),该资源被称为共享资源.如何保证多个线程访问到的数据是一致的,则被称为数据同步或资源同步. 2.线程通信 线程通信,又叫进程内通信, ...