synchronized是用来解决多线程情况下的线程安全问题的,它可以修饰方法也可以修饰语句块 ,

那么什么情况下是线程安全和线程不安全呢 ?

方法内的变量是线程安全的 , 类的实例变量是非线程安全的

首先来看一个非线程安全的例子

public class HasSefPrivateNum {
private int num;
public void addNum(String userName) throws InterruptedException{
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("num="+num);
}
} public class ThreadA extends Thread {
private HasSefPrivateNum numRef;
public ThreadA(HasSefPrivateNum numRef) {
this.numRef = numRef;
} @Override
public void run() {
super.run();
try {
numRef.addNum("a");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public class ThreadB extends Thread {
private HasSefPrivateNum numRef;
public ThreadB(HasSefPrivateNum numRef) {
this.numRef = numRef;
} @Override
public void run() {
super.run();
try {
numRef.addNum("b");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public class Main {
public static void main(String[] args) {
HasSefPrivateNum numRef = new HasSefPrivateNum();
ThreadA t1 = new ThreadA(numRef);
t1.start();
ThreadB t2 = new ThreadB(numRef);
t2.start();
}
}

结果如下,出现非线程安全的问题

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

此时synchronized派上用场了,只要在addNum方法上synchronized,就能避免非线程安全问题

public class HasSefPrivateNum {
private int num;
public synchronized void addNum(String userName) throws InterruptedException{
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("num="+num);
}
}

结果如下:

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

那么synchronized是怎么实现同步的呢

1. synchronized用在实例方法上那么它是持有对象锁,而不是把一段代码或方法当作锁,所以必须使用同一个对象当作锁才能达到同步的效果;

2. synchronized用在静态方法上那么它是对当前java文件对应的class类持锁。

首先看下synchronized修饰实例方法

public class Main {
public static void main(String[] args) {
HasSefPrivateNum numRef1 = new HasSefPrivateNum();
HasSefPrivateNum numRef2 = new HasSefPrivateNum();
ThreadA t1 = new ThreadA(numRef1);
t1.start();
ThreadB t2 = new ThreadB(numRef2);
t2.start();
}
}

结果如下,没有发生同步,因为两个线程拥有两个不同对象的锁

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

synchronized解决脏读问题

public class PublicVar {
private String username="A";
private String password="AA";
public synchronized void setValue(String username,String password){
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name=" + Thread.currentThread().getName()
+"username:"+username+",password:"+password); } catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue(){
System.out.println("getValue method thread name=" + Thread.currentThread().getName()
+"username:"+username+",password:"+password);
}
} public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B", "BB");
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
PublicVar publicVar = new PublicVar();
ThreadA t1 = new ThreadA(publicVar);
t1.start();
Thread.sleep(2000);
publicVar.getValue();
}
}

结果如下,get方法出现脏读的情况,原因是getValue方法不是同步方法,只需将getValue方法变成同步方法后就可以解决这个问题

getValue method thread name=mainusername:B,password:AA
setValue method thread name=Thread-0username:B,password:BB

对同步方法总结:

1. synchronized方法是持有对象锁,对相同的对象的synchronized方法会进行同步执行

2. 对于同一个对象锁内不同的synchronized方法持有同一个锁,即A线程调用了synchronized方法X,那么其他线程就不能调用其他的synchronized方法,直到synchronized方法x执行完后,但是可以调用其他非synchronized方法

3.synchronized方法支持锁重入,自己可以再次获得自己的内部锁,即当一个线程获得synchronized方法执行权后,在该方法内能调用该对象其他synchronized方法,如果不能重入,将会造成死锁。

4. synchronized方法出现异常,锁自动释放。

5. synchronized方法不具有继承性

使用同步代码块实现同步

synchronized的另一种用法就是同步代码块,和synchronized方法相比,它有两个有点

1. 它可以只针对方法内需要同步的代码进行同步,缩小同步范围,提高效率。

2. 它可以在同一个类中针对不同对象进行同步,提高效率。

public class Task {
private String getData1;
private String getdate2; public void doLongTimeTask(){
try {
System.out.println("begin task");
Thread.sleep(3000);
String privateGetData1 = "返回值1 thread name="+Thread.currentThread().getName();
String privateGetData2 = "返回值2 thread name="+Thread.currentThread().getName(); synchronized (this) {
getData1 = privateGetData1;
getdate2 = privateGetData2;
}
System.out.println(getData1);
System.out.println(getdate2);
System.out.println("end task");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private Task task;
public ThreadA(Task task) {
this.task = task;
}
@Override
public void run() {
super.run();
task.doLongTimeTask();
}
} public class ThreadB extends Thread {
private Task task;
public ThreadB(Task task) {
this.task = task;
}
@Override
public void run() {
super.run();
task.doLongTimeTask();
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
ThreadA t1 = new ThreadA(task);
t1.start();
ThreadB t2 = new ThreadB(task);
t2.start();
}
}

测试结果:当一个线程访问object的同步代码块时,另一个线程可以访问该object的非同步代码块。

begin task
begin task
返回值1 thread name=Thread-0
返回值2 thread name=Thread-1
返回值1 thread name=Thread-1
返回值2 thread name=Thread-1
end task
end task

另外synchronized(this)是锁定当前对象的,故和synchronized方法有相同的作用

1. synchronized同步方法

  1)对其他synchronized同步方法或者synchronized(this)同步代码块调用呈现阻塞状态

  2)同一时间内只有一个线程可以执行synchronized同步方法中的代码

2.synchronized(this)同步代码块

  1)对其他synchronized同步方法或者synchronized(this)同步代码块调用呈现阻塞状态

  2)同一时间内只有一个线程可以执行synchronized(this)同步方法中的代码

将任意对象作为对象监视器

public class Service {
public void test(LockObject object){
synchronized (object) {
try {
System.out.println("getLock time="+System.currentTimeMillis()+"thread name="+Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("releaseLock time="+System.currentTimeMillis()+"thread name="+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public class ThreadA extends Thread {
private Service service;
private LockObject object;
public ThreadA(Service service,LockObject object) {
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.test(object);
}
} public class ThreadB extends Thread {
private Service service;
private LockObject object;
public ThreadB(Service service,LockObject object) {
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.test(object);
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
LockObject object = new LockObject();
ThreadA t1 = new ThreadA(service,object);
t1.start();
ThreadB t2 = new ThreadB(service,object);
t2.start();
}
}

测试结果:同步调用,原因是使用了同一个对象监视器

getLock time=1516085547986thread name=Thread-0
releaseLock time=1516085550987thread name=Thread-0
getLock time=1516085550987thread name=Thread-1
releaseLock time=1516085553987thread name=Thread-1

如果不使用同一个对象监视器,则会出现异步的情况

public class Main {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
LockObject object1 = new LockObject();
LockObject object2 = new LockObject();
ThreadA t1 = new ThreadA(service,object1);
t1.start();
ThreadB t2 = new ThreadB(service,object2);
t2.start();
}
}
getLock time=1516085648021thread name=Thread-0
getLock time=1516085648021thread name=Thread-1
releaseLock time=1516085651021thread name=Thread-0
releaseLock time=1516085651022thread name=Thread-1

最后要理解synchronized,其实最主要的是理解synchronized方法和synchronized代码块使用的是什么对象监视器,它们是通过锁定对象监视器来实现同步功能的

1. synchronized同步实例方法和synchronized(this)的对象监视器是对象,它将持有对象锁

2. synchronized同步静态实例方法和synchronized(class)的对象监视器是class文件,它将持有Class锁

java多线程编程之synchronized的更多相关文章

  1. Java并发编程之synchronized

    在Java编程中,为了保证线程安全,有3种不同的思路1.互斥同步:包括synchronized和lock等. 2.非阻塞同步:如AtomicInteger的increaseAndGet()方法等. 3 ...

  2. Java并发编程之synchronized关键字

    整理一下synchronized关键字相关的知识点. 在多线程并发编程中synchronized扮演着相当重要的角色,synchronized关键字是用来控制线程同步的,可以保证在同一个时刻,只有一个 ...

  3. Java 多线程编程之:notify 和 wait 用法

    wait 和 notify 简介 wait 和 notify 均为 Object 的方法: Object.wait() —— 暂停一个线程 Object.notify() —— 唤醒一个线程 从以上的 ...

  4. Java 多线程编程之九:使用 Executors 和 ThreadPoolExecutor 实现的 Java 线程池的例子

    线程池用来管理工作线程的数量,它持有一个等待被执行的线程的队列.         java.util.concurrent.Executors 提供了 java.util.concurrent.Exe ...

  5. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  6. Java高性能编程之CAS与ABA及解决方法

    Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...

  7. iOS多线程编程之NSThread的使用

      目录(?)[-] 简介 iOS有三种多线程编程的技术分别是 三种方式的有缺点介绍 NSThread的使用 NSThread 有两种直接创建方式 参数的意义 PS不显式创建线程的方法 下载图片的例子 ...

  8. iOS多线程编程之NSThread的使用(转)

    本文由http://blog.csdn.net/totogo2010/原创 1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation  ...

  9. [转] iOS多线程编程之NSOperation和NSOperationQueue的使用

    <iOS多线程编程之NSThread的使用> 介绍三种多线程编程和NSThread的使用,这篇介绍NSOperation的使用. 使用 NSOperation的方式有两种, 一种是用定义好 ...

随机推荐

  1. java中“53”个关键字(含2个保留字)

    1.java的关键字(keyword)有多少个? 51+2个保留字=53个关键字(java的关键字都是小写的!!) 2.java的保留字(reserve word)有多少个?问题:分别是什么? 2个保 ...

  2. Jenkins里jobs和workspace什么区别

    https://segmentfault.com/q/1010000012575095/a-1020000012590560 简单的说,job 中保存的是项目是在 jenkins 上的配置.日志.构建 ...

  3. mstsc windows7/10远程桌面身份验证错误 要求的函数不受支持

    之前好好的能远程桌面连接到服务器,但是今天来就不能连接上了,并提示:身份验证错误.要求的函数不受支持. 猜想可能是Windows又更新了什么鬼,后面查询资料知道是由于CredSSP加密Oracle修正 ...

  4. bootstrapValidator代码中开启验证和判断验证是否通过

    //开启验证 $('#saveadmin_form').data('bootstrapValidator').validate(); //是否通过校验 if(!$('#saveadmin_form') ...

  5. php第二十节课

    JSON弹窗 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...

  6. enote笔记语言(4)

    what:我想知道某个“关键词(keyword)”(即,词语,可以是概念|专业术语|.......)的定义. why:我想知道事物发生的原因:我会不会犯“归因错误”?是“单因素”的还是“多因素”的原因 ...

  7. 洛谷——P1966 火柴排队&&P1774 最接近神的人_NOI导刊2010提高(02)

    P1966 火柴排队 这题贪心显然,即将两序列中第k大的数的位置保持一致,证明略: 树状数组求逆序对啦 浅谈树状数组求逆序对及离散化的几种方式及应用 方法:从前向后每次将数插入到bit(树状数组)中, ...

  8. 基于Arduino的音乐动感节奏灯

    1.音乐动感节奏灯是个什么东西? 前段时间听音乐觉得无聊,便想着音乐光听也没意思啊,能不能 “看见” 音乐呢?于是谷歌了一番,发现还真有人做了将音乐可视化的东西,那就是音乐节奏灯.说的简单点就是LED ...

  9. JPA学习(基于hibernate)

    参考博客:https://blog.csdn.net/baidu_37107022/article/details/76572195 常用注解: https://blog.csdn.net/eastl ...

  10. wait-notify模型面试题

    一道面试题: 启动两个线程, 一个输出 1,3,5,7-99, 另一个输出 2,4,6,8-100 最后 STDOUT 中按序输出 1,2,3,4,5-100 错误实现1: public class ...