synchronized关键字

synchronized是进行同步处理而保证线程安全。在一个方法中,如果是方法内的私有变量,那个这个变量是线程安全的,但是类中的实例变量是可能会出现线程安全问题的,当多个线程对这个实例变量进行修改,就可能会出现结果并不是我们期望的结果。

线程安全问题

下面一段代码就出现了线程安全问题。

本来当username为a的时候,num应该为100,但是由于设置让t1休眠了2秒,导致num被刷新成了200,导致最后输出时a和b的num都是200。

public class Service {
private int num = 0;
public void add(String username) {
try {
if (username.equals("a")) {
num = 100;
Thread.sleep(2000);
} else {
num = 200;
}
System.out.println(username + " " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.add("a");
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.add("b");
}
} public class Test {
public static void main(String[] args) {
Service service = new Service();
ThreadA t1 = new ThreadA(service);
ThreadB t2 = new ThreadB(service);
t1.start();
t2.start();
}
}

运行结果:

synchronized锁

下面给add方法加个synchronized关键字。

可以看到输出正确。

synchronized进行了同步,使得线程按照顺序进行访问,由于线程t1和t2的监视器都是同一个实例,相当于拥有同一个锁对象,所以可以进行同步访问。

public class Service {
private int num = 0;
public synchronized void add(String username) {
try {
if (username.equals("a")) {
num = 100;
Thread.sleep(2000);
} else {
num = 200;
}
System.out.println(username + " " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

输出结果:

synchronized同步代码块

上面的代码中是使用synchronized将整个方法进行上锁,只有当一个方法执行完毕后,另一个线程才可以执行这个方法,这样会导致性能损耗很大。

返回到线程安全问题,目的其实是为了解决对临界资源访问的问题,所以其实只需要将临界资源进行上锁就可以了,其他部分其实是可以异步进行的。

在下面代码中,doSomeTask方法里前半部分没有进行同步,后面使用了同步代码块进行加锁。

从输出结果可以看到,前面部分A、B两个线程是异步进行访问的,后部分是同步进行访问的。

public class Service {
public void doSomeTask(String username) {
for (int i = 0; i < 5; i++) {
System.out.println("没有同步 " + Thread.currentThread().getName() + " " + i);
}
System.out.println();
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println("同步了" + Thread.currentThread().getName() + " " + i);
}
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.doSomeTask("a");
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.doSomeTask("b");
}
} public class Test {
public static void main(String[] args) {
Service service = new Service();
ThreadA t1 = new ThreadA(service);
ThreadB t2 = new ThreadB(service);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}

运行结果:

所以使用synchronized同步代码块可以将需要加锁的部分进行上锁就行了,这样可以提高性能。

可以使用synchronized同步代码块加锁临界资源,这样就可以避免出现线程安全问题。

在JDK1.8中的ConcurrentHashMap也使用synchronized锁,synchronized锁的性能已经有了很大的提高。

static synchronized

static方法为类方法,那么对static方法加上synchronized锁后呢?加锁的究竟是什么呢?

其实这时候监视器上锁的对象为这个类对象,而不是一个具体的实例对象,就是所有该类的实例访问这个方法都会进行加锁。

从下面实例可以看出,虽然是两个实例四个线程访问该方法,但是还是进行了同步,因为所有实例访问的是同一把锁,也就是Service类的对象锁,只要是监视器锁对象是同一个,那么都是会进行上锁同步的。

public class Service {
public static synchronized void doSomeTask(String username) {
for (int i = 0; i < 5; i++) {
System.out.println("同步了" + Thread.currentThread().getName() + " " + i);
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.doSomeTask("a");
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.doSomeTask("b");
}
} public class Test {
public static void main(String[] args) {
Service service = new Service();
Service service1 = new Service();
ThreadA t1 = new ThreadA(service);
ThreadB t2 = new ThreadB(service);
ThreadA t3 = new ThreadA(service1);
ThreadB t4 = new ThreadB(service1);
t1.setName("A");
t2.setName("B");
t3.setName("C");
t4.setName("D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}

运行结果:

使用同步代码块也可以实现上述功能。

在同步代码块中,将监视的锁对象设置为Service.class,代表监视的是这个类锁,所以也对这个类进行加锁同步。

public class Service {
public void doSomeTask(String username) {
synchronized (Service.class) {
for (int i = 0; i < 5; i++) {
System.out.println("同步了" + Thread.currentThread().getName() + " " + i);
}
}
}
}

synchronized与static synchronized一起使用

四个线程访问两个方法,线程A和C访问task1,是一个普通的synchronized方法,线程B和D访问task2,是一个static synchronized方法。

从输出结果中可以看出,A、C进行了同步,B、D也进行同步,但是B和C在一开始是交替出现输出,代表B和C其实没有同步,就证明它们的锁不是同一把锁

A和C线程获取的实例的对象锁,而B和D线程获取的是这个类的锁。

public class Service {
public synchronized void task1() {
for (int i = 0; i < 5; i++) {
System.out.println("task1 " + Thread.currentThread().getName() + " " + i);
}
} public static synchronized void task2() {
for (int i = 0; i < 5; i++) {
System.out.println("task2 " + Thread.currentThread().getName() + " " + i);
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.task1();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
} @Override
public void run() {
super.run();
service.task2();
}
} public class Test {
public static void main(String[] args) {
Service service = new Service();
ThreadA t1 = new ThreadA(service);
ThreadB t2 = new ThreadB(service);
ThreadA t3 = new ThreadA(service);
ThreadB t4 = new ThreadB(service);
t1.setName("A");
t2.setName("B");
t3.setName("C");
t4.setName("D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}

运行结果:

综上:

  • synchronized同步代码块的性能比synchronized同步方法性能好。
  • 线程间的同步主要是看的获取的是什么锁,只有需要获取同一把锁的线程才会进行同步。
  • static synchronized获取的是类对象锁,而普通的synchronized获取的是实例对象的锁,所以其实他们不是同一把锁。
  • static synchronized的类对象锁可以对所有该类实例进行同步,因为所有该类实例获取的类锁都是同一把锁。

浅析Java中synchronized与static synchronized的更多相关文章

  1. synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

    本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁 ...

  2. Java面试之synchronized 和 static synchronized

    ​面试题: 答案: 不能 不能 不能 不能 能 正文 概述 通过分析这两个用法的分析,我们可以理解java中锁的概念.一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念), ...

  3. Java中的ReentrantLock和synchronized两种锁定机制的对比

    问题:多个访问线程将需要写入到文件中的数据先保存到一个队列里面,然后由专门的 写出线程负责从队列中取出数据并写入到文件中. http://blog.csdn.net/top_code/article/ ...

  4. Synchronized和Static Synchronized区别

    通过分析这两个用法的分析,我们可以理解Java中锁的概念. 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念), 一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线 ...

  5. [Android Pro] synchronized与static synchronized 的区别

    reference to :  http://www.cnblogs.com/shipengzhi/articles/2223100.html 1.synchronized与static synchr ...

  6. synchronized和static synchronized的比较

    群里讨论的一个问题,网上别人已经贴出了很详细的说明,这里补充记录下,后面加入个人测试代码. 起因:1月份的时候看群里讨论一道问题,问题内容如下: 一个日本作者-结成浩的<java多线程设计模式& ...

  7. synchronized与static synchronized 差异

    1.synchronized与static synchronized 差异       synchronized是对类的当前实例进行加锁,防止其它线程同一时候訪问该类的该实例的全部synchroniz ...

  8. 多线程同步锁和死锁以及synchronized与static synchronized 的区别

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程.一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序.简而言之:一个程序运行后至少有一个进程,一个进程 ...

  9. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

随机推荐

  1. 一期结业KTV项目难点

    查询完成后给歌曲便利到一个数组当中将选中的歌曲对象加入到playlist类中的数组songs. 创建类名称: class Playlist       {   //定义一个长度为50的数组       ...

  2. 我个人的Java学习经验(一家之言)

    声明:本文只是我的个人经验之谈,或者连经验之谈都算不上,因为我觉得自己还是个新手,没有什么经验可谈,就算是我分享一下自己从开始学习Java到现在的一些心路历程吧,各位看官暂且看吧,欢迎交流.第一部分算 ...

  3. index_merge引发的死锁排查

    概述 前几天排查了一个死锁问题,最开始百思不得其解,因为发生死锁的两个事务是单语句事务,语句类型相同(where属性列相同,仅值不同),而且语句都走了相同的索引,但最终确实发生了死锁.通过定位排查发现 ...

  4. JNI调用的helloworld(JNI_OnLoad映射方式)

    本示例展示JNI的基本示例,helloworld级别的,不过是用JNI_OnLoad映射的方式. 直接看代码,先看包含native method的Person.java的代码: package hel ...

  5. 【转】对于HttpClient和HtmlUnit的理解

    原文地址:http://www.haohaoblog.com/?p=1327&utm_source=tuicool 做Java编程的人其实,很多不懂SEO,也不知道如何让百度收录等等,当然,对 ...

  6. AIO75产品特征与优势

    第一章 系统一体化 AIO7的核心流程由供应链.生产制造.财务成本及自动化办公构成,是迄今为止国内最完善的ERP.OA .HR .MES一体化产品.通过CRM(客户关系)及DRP(分销)扩充出“营销通 ...

  7. maven项目发布不成功的问题

    MyEclipes   里面有好多的项目,有些项目是插件有些是组件,  就是有些项目是被依赖的项目,有些事项目的主体,被依赖的项目需要打成jar 包放在maven的中央仓库里面,也是所说的maven的 ...

  8. Python学习路线图

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 Python学习路线图你可以通过百度云盘下载观看对应的视频 链接: http://pan.baidu.com/s/1c2zLllA ...

  9. 摆脱printf的噩梦

    众所周知,printf是一个方便.直观.易写.变长参数的打印函数,但它有一个致命的缺陷,如下的语句将导致程序出现严重的运行时错误: printf("%s", 1); 然后程序中断, ...

  10. git中常见的几个命令

    git中常见的几个命令 本地仓库 三个区域 工作目录 暂存区 本地仓库 文件的四个状态 未跟踪 untracked 已暂存 staged 已提交commited 已修改 modified 基本命令 g ...