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. ubuntu.16.04 安装.net core记录

    jack@ubuntu:~$ sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotne ...

  2. Photoshop如何实现图片相对画布居中

    先按ctrl+A,再选择要居中的图层,然后就会发现居中按钮被激活了

  3. Sprinboot优雅配置监听,并记录所有启动事件

    在阅读Springboot启动源码的时候,发现Springboot自动启动listeners是通过uopeizhi文件配置的,本文就是采用Springboot方式自动装入listeners. 项目依赖 ...

  4. 【LeetCode】3 、Longest Substring Without Repeating Characters

    题目等级:Medium 题目描述:   Given a string, find the length of the longest substring without repeating chara ...

  5. time模块和datatime模块

    一.time模块 time.time() 获取时间戳 time.sleep() 睡几秒 time.gmtime() utc时间元组 time.localtime() 本地时间元组 time.mktim ...

  6. P1352 没有上司的舞会——树形DP入门

    P1352 没有上司的舞会 题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员 ...

  7. UID中RUID、EUID和SUID的区别

    看UNIX相关的书时经常能遇到这几个概念,但一直没有好好去理清这几个概念,以致对这几个概念一直一知半解.今天好好区分了一下这几个概念并总结如下.说白了这几个UID引出都是为了系统的权限管理. 下面分别 ...

  8. [luogu4054 JSOI2009] 计数问题(2D BIT)

    传送门 Solution 2D BIT模板 Code //By Menteur_Hxy #include <cmath> #include <cstdio> #include ...

  9. gitlab分享项目到其它组

    1. 找到以下页面(管理员权限),随便点入一个项目 2. 点击edit编辑 3.点击members 4. 查看是否分享成功

  10. pandas - 案例(美国各州人口普查)

    需求: 导入文件,查看原始数据 将人口数据和各州简称数据进行合并 将合并的数据中重复的abbreviation列进行删除 查看存在缺失数据的列 找到有哪些state/region使得state的值为N ...