为什么要用锁?

在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的。注意这里 局部变量是不存在脏读的情况

实例:

定义一个类,通过不同的传入返回不同的结果 SynchronizedLock.java

 /**
* 使用synchronized关键字加锁
*
*/
public class SynchronizedLock {
private int num = 0; public void addNum(String userName) {
try {
if("a".equals(userName)){
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) {
}
}
public static void main(String[] args) {
SynchronizedLock sl=new SynchronizedLock();
MyThread01 mt1=new MyThread01(sl);
MyThread02 mt2=new MyThread02(sl); mt1.start();
mt2.start();
} }

自定义两个线程,MyThread01.javaMyThread02.java

 public class MyThread01 extends Thread {
private SynchronizedLock sl; public MyThread01(SynchronizedLock sl) {
this.sl = sl;
} @Override
public void run() {
sl.addNum("a");
}
}
 public class MyThread02 extends Thread {
private SynchronizedLock sl; public MyThread02(SynchronizedLock sl) {
this.sl = sl;
} @Override
public void run() {
sl.addNum("b");
}
}

运行SynchronizedLock的结果为:

a set over
b set over
b,num=200
a,num=200

如果按照代码执行的话,应该打印 “a,num=100”和“b,num=200”,但是结果却出现了问题。这就是线程的安全问题。过程如下:

1、mt0先运行,给num赋值100,然后打印出"a set over!",开始睡觉

2、mt0在睡觉的时候,mt1运行了,给num赋值200,然后打印出"b set over!",然后打印"b num = 200"

3、mt1睡完觉了,由于mt0的num和mt1的num是同一个nu

使用synchronized关键字

     public synchronized void addNum(String userName) {
try {
if("a".equals(userName)){
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) {
}
}

结果为:

a set over
a,num=100
b set over
b,num=200

多个对象

如果在有Synchronized锁的时候,使用多个对象呢?

     public static void main(String[] args) {
SynchronizedLock sl=new SynchronizedLock();
SynchronizedLock sl2=new SynchronizedLock();
MyThread01 mt1=new MyThread01(sl);
MyThread02 mt2=new MyThread02(sl2); mt1.start();
mt2.start();
}

结果为:

a set over
b set over
b,num=200
a,num=100

原因:

关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,这里如果是把一段代码或方法(函数)当作锁,其实获取的也是对象锁,只是监视器(对象)不同而已,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,其他线程都只能呈等待状态。但是这有个前提:既然锁叫做对象锁,那么势必和对象相关,所以多个线程访问的必须是同一个对象

如果多个线程访问的是多个对象,那么Java虚拟机就会创建多个锁,就像上面的例子一样,创建了两个ThreadDomain13对象,就产生了2个锁。既然两个线程持有的是不同的锁,自然不会受到"等待释放锁"这一行为的制约,可以分别运行addNum(String userName)中的代码。

1、A线程持有Object对象的Lock锁,B线程可以以异步方式调用Object对象中的非synchronized类型的方法

2、A线程持有Object对象的Lock锁,B线程如果在这时调用Object对象中的synchronized类型的方法则需要等待,也就是同步

synchronized锁重入

关键字synchronized拥有锁重入的功能。所谓锁重入的意思就是:当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁的

 /**
* 多个synchronized关键字
*
*/
public class SynchronizedLocks {
public synchronized void print1() {
System.out.println("pirnt 1 method");
print2();
} private synchronized void print2() {
System.out.println("print 2 method");
print3();
} private synchronized void print3() {
System.out.println("print 3 method");
}
public static void main(String[] args) {
MyThread03 mt3=new MyThread03();
mt3.start();
} }
class MyThread03 extends Thread{
@Override
public void run() {
SynchronizedLocks sls=new SynchronizedLocks();
sls.print1();
}
}

结果:

pirnt 1 method
print 2 method
print 3 method

证明了对象可以再次获取自己的内部锁。这种锁重入的机制,也支持在父子类继承的环境中

异常自动释放锁

当一个线程执行的代码出现异常时,其所持有的锁会自动释放

模拟的是把一个long型数作为除数,从MAX_VALUE开始递减,直至减为0,从而产生ArithmeticException。看一下例子:SynchronizedLockException.java

 /**
* 异常释放锁
*/
public class SynchronizedLockException {
public static void main(String[] args) {
SynchronizedLockException sle = new SynchronizedLockException();
MyThread04 mt1=new MyThread04(sle);
MyThread04 mt2=new MyThread04(sle);
mt1.start();
mt2.start();
} public synchronized void testMethod(){
try {
System.out.println("testMethod(),currentThread:"+Thread.currentThread().getName());
long l=Integer.MAX_VALUE;
while(true){
long lo=2/l;
l--;
}
} catch (Exception e) {
e.printStackTrace();
}
} }
class MyThread04 extends Thread{
private SynchronizedLockException sle;
public MyThread04(SynchronizedLockException sle){
this.sle=sle;
}
@Override
public void run() {
sle.testMethod();
}
}

打印结果:

testMethod(),currentThread:Thread-0
java.lang.ArithmeticException: / by zero
testMethod(),currentThread:Thread-1
at com.stu.lockthread.SynchronizedLockException.testMethod(SynchronizedLockException.java:17)
at com.stu.lockthread.MyThread04.run(SynchronizedLockException.java:33)
java.lang.ArithmeticException: / by zero
at com.stu.lockthread.SynchronizedLockException.testMethod(SynchronizedLockException.java:17)
at com.stu.lockthread.MyThread04.run(SynchronizedLockException.java:33)

关于Synchronized关键字

一 原子性(互斥性):实现多线程的同步机制,使得锁内代码的运行必需先获得对应的锁,运行完后自动释放对应的锁。

二 内存可见性:在同一锁情况下,synchronized锁内代码保证变量的可见性。

三 可重入性:当一个线程获取一个对象的锁,再次请求该对象的锁时是可以再次获取该对象的锁的。

如果在synchronized锁内发生异常,锁会被释放。

总结:

(1)synchronized方法 与 synchronized(this) 代码块 锁定的都是当前对象,不同的只是同步代码的范围

(2)synchronized (非this对象x) 将对象x本身作为“对象监视器”:

a、多个线程同时执行 synchronized(x) 代码块,呈现同步效果。

b、当其他线程同时执行对象x里面的 synchronized方法时,呈现同步效果。

c、当其他线程同时执行对象x里面的 synchronized(this)方法时,呈现同步效果。

(3)静态synchronized方法 与 synchronized(calss)代码块 锁定的都是Class锁。Class 锁与 对象锁 不是同一个锁,两者同时使用情况可能呈异步效果。

(4)尽量不使用 synchronized(string),是因为string的实际锁为string的常量池对象,多个值相同的string对象可能持有同一个锁。

[多线程] 线程中的synchronized关键字锁的更多相关文章

  1. 深入理解java中的synchronized关键字

    synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D ...

  2. java并发之线程同步(synchronized和锁机制)

    使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...

  3. 巨人大哥谈Java中的Synchronized关键字用法

    巨人大哥谈Java中的Synchronized关键字用法 认识synchronized 对于写多线程程序的人来说,经常碰到的就是并发问题,对于容易出现并发问题的地方价格synchronized基本上就 ...

  4. 多线程总结-同步之synchronized关键字

    目录 1.为什么要使用synchronized? 2.synchronized锁什么,加锁的目的是什么? 3.代码示例 3.1锁this和临界资源对象 3.2锁class类对象 3.3 什么时候锁临界 ...

  5. java高并发系列 - 第10天:线程安全和synchronized关键字

    这是并发系列第10篇文章. 什么是线程安全? 当多个线程去访问同一个类(对象或方法)的时候,该类都能表现出正常的行为(与自己预想的结果一致),那我们就可以所这个类是线程安全的. 看一段代码: pack ...

  6. Java进程与多线程+线程中的join、yield、wait等方法+synchronized同步锁使用

    首先了解什么是多线程与进程 进程:是一个执行过程,动态的概念 --->会分配内存线程:是进程的一个单元,线程是系统最小的执行单元 详解: http://blog.csdn.net/luoweif ...

  7. 多线程,线程同步,synchronized关键字的用法

    一.什么是多线程 Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorSe ...

  8. Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)

    关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...

  9. JAVA synchronized关键字锁机制(中)

    synchronized 锁机制简单的用法,高效的执行效率使成为解决线程安全的首选. 下面总结其特性以及使用技巧,加深对其理解. 特性: 1. Java语言的关键字,当它用来修饰一个方法或者一个代码块 ...

随机推荐

  1. 【Spark-core学习之一】 Spark初识

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...

  2. ANNOTATION 注解

    注解(Annotation)很重要,未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是 ...

  3. LeetCode258 各位相加

    题目链接:https://leetcode-cn.com/problems/add-digits/ 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数. 示例: 输入: 38 输出: ...

  4. “Nested exception: 前言中不允许有内容"错误处理

    最近在做一个小项目,使用org.dom4j.DocumentHelper.parseText方法时一直报错”Nested exception: 前言中不允许有内容",这个parseText解 ...

  5. centos7.3配置python2、3环境与配置各自pip

    环境:CentOS-7-x86_64-Everything-1611 No.1 查看CentOS对Python的默认依赖 [root@cs ~]# ls /usr/bin/python* /usr/b ...

  6. Hopfield神经网络

    神经网络分类 多层神经网络:模式识别 相互连接型网络:通过联想记忆去除数据中的噪声 1982年提出的Hopfield神经网络是最典型的相互连结型网络. 联想记忆 当输入模式为某种状态时,输出端要给出与 ...

  7. webpack 几个基本打包扩展项的安装命令

    网速比较慢的童鞋,装包时请准备好花生瓜子 webpack 本身只能打包 JavaScript 模块,但是它可以通过拓展打包比如静态资源文件.less.sass.typescript 等,还可以通过 b ...

  8. Oracle错误——user ** lacks CREATE SESSION privilege logon denied

    错误 在删除一个用户TEST的情况下,再次新建用户TEST并赋予sysdba权限,使用plsqldev工具登录数据库TEST用户,报错user TEST  lacks CREATE SESSION p ...

  9. Python3 tkinter基础 Menubutton 点击按钮出现下拉菜单

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  10. centos linux查看硬盘型号

    [root@]# smartctl --all /dev/sdasmartctl 6.5 2016-05-07 r4318 [x86_64-linux-3.10.0-957.1.3.el7.x86_6 ...