为什么要用锁?

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

实例:

定义一个类,通过不同的传入返回不同的结果 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. python locust 性能测试:locust 参数化(list) ---循环取数据,数据可重复使用

    from locust import TaskSet, task, HttpLocust class UserBehavior(TaskSet): def on_start(self): # 当模拟用 ...

  2. MySQL外键约束_ON DELETE CASCADE/ON UPDATE CASCADE

    MySQL通过外键约束实现数据库的参照完整性,外键约束条件可在创建外键时指定,table的存储引擎只能是InnoDB,因为只有这种存储模式才支持外键. 外键约束条件有以下4种: (1)restrict ...

  3. Linux下系统时间函数、DST等相关问题总结(转)

    Linux下系统时间函数.DST等相关问题总结 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根 ...

  4. php 使用str_replace替换关键词(兼容字符串,一维数组,多维数组)

    通过递归的方式来实现替换字符串. /* * * 使用str_replace替换关键词(兼容字符串,一维数组,多维数组) * $search 需要查找的内容 * $replace 需要替换的内容 * $ ...

  5. git 分支命名规范

    为规范开发,保持代码提交记录以及 git 分支结构清晰,方便后续维护,现规范 git 的相关操作. 主要规范两点: git 分支命名规范 git 提交记录规范 1. git 分支命名规范 git 分支 ...

  6. python 的运行方式和基础变量

    机器语言:直接用计算机能听的懂得二进制去编写程序,需要了解硬件细节 优点:执行效率高 缺点:开发效率低汇编语言:用英文单词取代二进制指令去编写程序 优点:开发效率高于机器语言 缺点:执行效率低于机器语 ...

  7. ssm整合(方案一 引入jar)

    最近为了研究redis整合,基本都是ssm+redis 好吧 我是老古董了,以前都是ssi,那就折腾一下ssm先,具体方案: 方案一:基本引入jar版.方案二:maven版(后续继续整) 这篇主要是通 ...

  8. 2017 Russian Code Cup (RCC 17), Final Round

    2017 Russian Code Cup (RCC 17), Final Round A Set Theory 思路:原题转换一下就是找一个b数组,使得b数组任意两个数的差值都和a数组任意两个数的差 ...

  9. jTimer

      很多时候我们需要按时间间隔执行一个任务,当满足一定条件时停止执行.此插件旨在解决这一经常遇到的问题. jTimer: (function ($) { $.extend({ timer: funct ...

  10. React文档(十一)提升state

    经常有些组件需要映射同一个改变的数据.我们建议将共用的state提升至最近的同一个祖先元素.我们来看看这是怎样运作的. 在这一节中,我们会创建一个温度计算器来计算提供的水温是否足够沸腾. 我们先创建一 ...