为什么要用锁?

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

实例:

定义一个类,通过不同的传入返回不同的结果 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. configparser配置文件处理

    创建一个configparser格式的文档: import configparser config = configparser.ConfigParser()config["DEFAULT& ...

  2. java ==与equals()方法的总结

    1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolean   他们之间的比较,应用双等号(==),比较的是他们的值. 2.复合数据 ...

  3. iOS项目之WKWebView替换UIWebView相关

    在网上已经有了许多关于UIWebView替换为WKWebView的文章,所以在这里就不在多说替换的细节了,不会的可以在网上搜搜. 下面是我在项目中遇到的问题: 问题一:在UIWebView中,网页显示 ...

  4. MySQL5.7 Dockerfile

    #Dockerfile for mysql5.7 FROM centos COPY ["src","/src"] RUN groupadd -g 1003 my ...

  5. Keepalived原理与实战精讲--VRRP协议

    . 前言 VRRP(Virtual Router Redundancy Protocol)协议是用于实现路由器冗余的协议,最新协议在RFC3768中定义,原来的定义RFC2338被废除,新协议相对还简 ...

  6. Dockerfile构建容器---语法高亮

    三个文件扔进相关的目录即可 wget -O /usr/share/vim/vimfiles/doc/dockerfile.txt https://raw.githubusercontent.com/a ...

  7. webpack搭建项目时出现的报错“Module build failed (from ./node_modules/css-loader/dist/cjs.js) CssSyntaxError”

    控制台出现的错误如下: webpack.config.js中的配置如下: module.exports={ entry:'./src/main.js', output:{ path:__dirname ...

  8. P3327 [SDOI2015]约数个数和

    思路 做这题先要知道一个性质, \[ d_{ij}=\sum_{x|i}\sum_{y|j}[(x,y)=1] \] 然后上莫比乌斯反演颓柿子就好了 \[ \begin{align}&\sum ...

  9. Docker for windows10 配置阿里云镜像

    到官网下载 并且 安装 Docker for windows  (注意 官方要求 windows10 是企业版才行  天朝你懂的 ) 关于 Docker for windows  要求有  带有 hy ...

  10. JAVA中字符串比较equals()和equalsIgnoreCase()的区别

    1.使用equals( )方法比较两个字符串是否相等(区分大小写) 2.使用equalsIgnoreCase( )方法比较两个字符串是否相等(不区分大小写) boolean equalsIgnoreC ...