并发编程-synchronized 细节说明

1. synchronized-锁重入 & 异常释放锁 说明

  • * 关键字synchronized 拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象的锁后,再次请求此对象时可以再次得到该对象的锁;

2. synchronized-代码块 说明

  • * 使用synchronized声明的方法在某些情况下是比较极端的(存在弊端):线程A调用同步的方法执行一段很长时间的任务,那么B线程就必须等待比较长的时间才能执行;

解决方法:使用synchronized代码块去优化代码执行的时间,也就是减少锁的粒度;

  • * 特别注意一个问题:就是不要使用String的常量进行加锁,会出现死循环的问题。
  • * 锁对象的改变问题:当使用一个对象进行加锁的时候,要注意对象本身发生变化的时候,那么持有的锁就不同。
  • 如果对象本身不发生变化,那么依然是同步的,即使对象的属性发生变化也是同步的。

实例:
SyncDubbo1.java 和 SyncDubbo2.java

/**
* synchronized的重入
* @@author Maozw
*
*/
public class SyncDubbo1 { public synchronized void method1(){
System.out.println("method1..");
method2();
}
public synchronized void method2(){
System.out.println("method2..");
method3();
}
public synchronized void method3(){
System.out.println("method3..");
} public static void main(String[] args) {
final SyncDubbo1 sd = new SyncDubbo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
}
}
/**
* synchronized的重入
* @@author Maozw
*
*/
public class SyncDubbo2 { static class Main {
public int i = 10;
public synchronized void operationSup(){
try {
i--;
System.out.println("Main print i = " + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} static class Sub extends Main {
public synchronized void operationSub(){
try {
while(i > 0) {
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(100);
this.operationSup();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
}); t1.start();
}
}

出现异常会释放锁

示例:SyncException.java
说明:对于web程序,异常释放锁的情况,如果来不及及时处理,很可能对应用程序的业务逻辑产出错误:如执行一个队列任务,很多对象都去等待第一个对象执行完成并释放锁,但是第一个对象由于异常原因,导致业务逻辑没有正常执行完成,就释放了锁,那么后续任务就会产生一些问题;所以这个问题需要注意;

/**
* synchronized异常
* @@author Maozw
*
*/
public class SyncException { private int i = 0;
public synchronized void operation(){
while(true){
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if(i == 20){
//Integer.parseInt("a");
throw new RuntimeException();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}

synchronized-代码块 说明

使用synchronized声明的方法在某些情况下是比较极端的(存在弊端):线程A调用同步的方法执行一段很长时间的任务,那么B线程就必须等待比较长的时间才能执行;
* 解决方法:使用synchronized代码块去优化代码执行的时间,也就是减少锁的粒度;
* 示例:ObjectLock.java
* 说明:synchronized可以使用任务的Object对象进行加锁,用法比较灵活;

 /**
* 使用synchronized代码块加锁,比较灵活
* @@author Maozw
*
*/
public class ObjectLock { public void method1(){
synchronized (this) { //对象锁
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void method2(){ //类锁
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private Object lock = new Object();
public void method3(){ //任何对象锁
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
}); t1.start();
t2.start();
t3.start();
}
}

特别注意一个问题:就是不要使用String的常量进行加锁,会出现死循环的问题。

* 示例:StringLock.java
* 说明:

/**
* synchronized代码块对字符串的锁,注意String常量池的缓存功能
* @@author Maozw
*
*/
public class StringLock { public void method() {
//new String("字符串常量")
synchronized ("字符串常量") {
try {
while(true){
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2"); t1.start();
t2.start();
}
}

锁对象的改变问题:

* 说明:

  • 1. 当使用一个对象进行加锁的时候,要注意对象本身发生变化的时候,那么持有的锁就不同。
  • 2. 如果对象本身不发生变化,那么依然是同步的,即使对象的属性发生变化也是同步的。

* 示例:ModifyLock.java

  /**
* 同一对象属性的修改不会影响锁的情况
* @@author Maozw
*
*/
public class ModifyLock { private String name ;
private int age ; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public synchronized void changeAttributte(String name, int age) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始");
this.setName(name);
this.setAge(age); System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: "
+ this.getName() + ", " + this.getAge()); Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
final ModifyLock modifyLock = new ModifyLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("张三", 20);
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("李四", 21);
}
},"t2"); t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

死锁问题 :

* 说明: 在设计程序时就应该避免双方相互持有对方的锁的情况
* 示例:

/**
* 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况
* @@author Maozw
*
*/
public class DeadLock implements Runnable{ private String tag;
private static Object lock1 = new Object();
private static Object lock2 = new Object(); public void setTag(String tag){
this.tag = tag;
} @Override
public void run() {
if(tag.equals("a")){
synchronized (lock1) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
}
}
}
if(tag.equals("b")){
synchronized (lock2) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
}
}
}
} public static void main(String[] args) { DeadLock d1 = new DeadLock();
d1.setTag("a");
DeadLock d2 = new DeadLock();
d2.setTag("b"); Thread t1 = new Thread(d1, "t1");
Thread t2 = new Thread(d2, "t2"); t1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

5.并发编程-synchronized 细节说明的更多相关文章

  1. 3.并发编程-ReentrantLock 细节说明

    并发编程-ReentrantLock 细节说明 ---title: 并发编程-ReentrantLock 细节说明date: 2018-07-05 09:06:57categories: - 并发编程 ...

  2. 并发编程——synchronized关键字的使用

    前言 我们一般对共享数据操作的时候,为了达到线程安全我们会使用synchronized关键字去修饰方法或者代码块.那么今天我们就来讲一讲synchronized关键字的使用. 专栏推荐: 并发编程专栏 ...

  3. Java并发编程-synchronized

    多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题.同步机制可以使用synchronized关键字实现.synchronized关 ...

  4. Java并发编程-synchronized指南

    在多线程程序中,同步修饰符用来控制对临界区代码的访问.其中一种方式是用synchronized关键字来保证代码的线程安全性.在Java中,synchronized修饰的代码块或方法不会被多个线程并发访 ...

  5. java并发编程--Synchronized的理解

    synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...

  6. 并发编程-synchronized关键字大总结

    0.synchronized 的特点: 可以保证代码的原子性和可见性. 1.synchronized 的性质: 可重入(可以避免死锁.单个线程可以重复拿到某个锁,锁的粒度是线程而不是调用).不可中断( ...

  7. Java并发编程 | Synchronized原理与使用

    Java提供了多种机制实现多线程之间有需要同步执行的场景需求.其中最基本的是Synchronized ,实现上使用对象监视器( Monitor ). Java中的每个对象都是与线程可以锁定或解锁的对象 ...

  8. 并发编程--CAS自旋锁

    在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识. 一.自旋锁提出的背景 由于在多处理器系 ...

  9. 【并发编程】【JDK源码】CAS与synchronized

    线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...

随机推荐

  1. Luogu P2915 [USACO08NOV]奶牛混合起来

    题外话: 是非常颓废的博主 写题解也不在于能不能通过啦,主要是缓解颓废 首先看到这个题,肯定是可以暴力搜索的: 不得不说这道题还是很善良的,一波大暴力dfs,居然有70pts: #include< ...

  2. python-day25(正式学习)

    目录 组合 多态 多态性 好处 封装 两个层面 property 组合 组合就是一个类的对象具备某一个属性,该属性的值是指向另外外一个类的对象 组合是用来解决类与类之间代码冗余的问题 首先我们先写一个 ...

  3. 一个php文件就可以把数据库的数据导出Excel表格

    数据库内容太多,复制粘贴太麻烦?那就用代码实现把,把代码写好了,导出还不容易吗,访问即可导出. excel.php <?php error_reporting(E_ALL ^ E_DEPRECA ...

  4. inline void 树状数组神奇感悟【雾

    才发现扫描线可以用树状数组搞... 致远星患者 (另外根据这篇博文的内容怎么越来越感觉自己往 PJ 入门靠拢了...) 还有一点,咱把树状数组当做线段树来康的话其实一切都会很清晰,这个来张四合一的图: ...

  5. Java学习:通过Scanner读取文件

    Scanner不仅能够读取用户的键盘输入,还可以读取文件输入. 需要在创建Scanner对象的时候传入一个File对象作为参数.代码如下: import java.util.Scanner; impo ...

  6. export ,export default 和 import 区别以及用法

    首先要知道export,import ,export default是什么 ES6模块主要有两个功能:export和importexport用于对外输出本模块(一个文件可以理解为一个模块)变量的接口i ...

  7. 【AST篇】教你如何编写 Eslint 插件

    前言 虽然现在已经有很多实用的 ESLint 插件了,但随着项目不断迭代发展,你可能会遇到已有 ESLint 插件不能满足现在团队开发的情况.这时候,你需要自己来创建一个 ESLint 插件. 本文我 ...

  8. redis常见问题和解决方案

    转载:https://www.cnblogs.com/aspirant/p/6820262.html [原创]那些年用过的Redis集群架构(含面试解析) redis常见问题和解决方案 持久化.主从问 ...

  9. poj 1543 Perfect Cubes (暴搜)

    Perfect Cubes Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 15302   Accepted: 7936 De ...

  10. 一、doT.js使用笔记

    一.赋值https://www.jianshu.com/p/19156f9fac1e <!DOCTYPE html> <html> <head> <meta ...