为什么要用锁?

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

实例:

定义一个类,通过不同的传入返回不同的结果 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取整及保留小数小结

    1.int() 向下取整 内置函数  n = 3.75 print(int(n))>>> 3 n = 3.25 print(int(n))>>> 3 2.round ...

  2. <转>jmeter(二十)阶梯式加压测试

    本博客转载自:http://www.cnblogs.com/imyalost/category/846346.html 个人感觉不错,对jmeter讲解非常详细,担心以后找不到了,所以转发出来,留着慢 ...

  3. python基础(四)集合

    [集合特点]  1.天生去重.循环 2 关系测试 -交集,差集,并集,(反向差集,对称差集) list = [1,2,3,4,5,3,6]list_2 =[2,3,5,7,8]list=set(lis ...

  4. 二进制包安装MYSQL——

    yum install libaio -y #安装mysql依赖包tar zxf mysql-5.5.59-linux-glibc2.12-x86_64.tar.gz mv mysql-5.5.59- ...

  5. liteos 从入门到放弃

    这两天收到一份sdk,hisi 3516cv200 liteos的sdk 正好手头有硬件,就随便编译玩玩. 解压sdk. Hi3518E_SDK_V5.0.5.0 ll@ubuntu:~/work20 ...

  6. Docker Swarm volume 数据持久化

    Docker Swarm volume 数据持久化 volume 是将宿主级的目录映射到容器中,以实现数据持久化. 可以用两种方式来实现: volume 默认模式:工作节点宿主机数据同步到容器内. v ...

  7. 160CrackMe第十九Brad Soblesky.2

    查壳无壳,vc写的. 我们输入假码后,然后点击,弹出错误框,直接打开od,对MessageBoxA下断点也行,寻找字符串也行. 一般的错误提示部分代码类似于这样. call xxx test xxx, ...

  8. 阿里技术专家详解Dubbo实践,演进及未来规划

    https://mp.weixin.qq.com/s/9rVGHYfeE8yM2qkSVd2yEQ

  9. Executors创建线程池的几种方式以及使用

    Java通过Executors提供四种线程池,分别为:   1.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.   ...

  10. [JavaScript-Function] Function Invocation/Call(函数调用) 以及call() and apply() 方法

    介绍:JS函数中的代码会被函数被invoke(调用)时执行. 函数被定义时代码不执行, 函数调用时函数内的代码会被执行. 常用的term是 call a function 而不是 invoke a f ...