线程安全问题

先看下面代码出现的问题:

定义一个Task类,里面有一个成员变量和一个有boolean类型参数的方法,方法内部会根据传入参数修改成员变量的值。

package com.sutaoyu.Thread;

public class Task {
//成员变量存储在堆内存里面,多个线程访问同一个堆内存,
//即多个线程可以同时修改num的值,这样会导致线程安全问题
private int num = 0;
public void changeNum(boolean flag) {
if(flag) {
num = 88;
System.out.println(Thread.currentThread().getName() + "======" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over")
}else {
System.out.println(Thread.currentThread().getName() + "=====" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over");
}
}
}

创建一个Task对象,将这个对象放到两个线程中,在这两个线程中分别调用changeNum方法

package com.sutaoyu.Thread;

public class SynchronizedTest01 {
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread() {
public void run() {
task.changeNum(true);
}
};
Thread t2 = new Thread() {
public void run(){
task.changeNum(false);
}
}; t1.start();
t2.start();
}
}

上面的代码有可能会出现打印这样的结果:

Thread-1=====begin
Thread-0=====begin
Thread-1=====66
Thread-0=====66
Thread-1=====over
Thread-0=====over

正常情况下应该打印出一个88一个66,可是上面却两个线程打印出的两个66,这样就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改。
程序执行分析:
cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。

内存图解:

同步和异步

比如你要给A,B,C三人发消息
同步:先给A发,等A回复后,再给B发,等B回复后,再给C发,排队等待
异步:直接给A,B,C发消息,中间不需要等某人回复之后再给其他人发消息,不用排队等待

使用synchronized将方法设置为同步

将Task中的changeNum方法设置为同步的

public synchronized void changeNum(boolean flag)

在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changeNum方法,t1线程先执行了这个方法,那么t1会先在Task对象上面加锁,加锁后,别的线程就无法执行当前Task对象上的changeNum方法,直到t1执行结束changeNum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changeNum方法,解决了线程安全问题。
注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题。如下代码:

package com.sutaoyu.Thread;

public class SynchronizedTest01 {
public static void main(String[] args) { Task task1 = new Task();
Task task2 = new Task(); Thread t1 = new Thread() {
public void run() {
task1.changeNum(true);
}
};
Thread t2 = new Thread() {
public void run(){
task2.changeNum(false);
}
}; t1.start();
t2.start();
}
}

58、synchronized同步方法的更多相关文章

  1. synchronized同步方法《二》

    1.synchronized方法和锁对象 (1).验证线程锁的是对象 代码如下: 1.1创建一个MyObject类: package edu.ymm.about_thread4; public cla ...

  2. synchronized同步方法

    “非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问的时候产生,产生的后果是脏读,也就是取到的数据是被更改过的.而“线程安全”就是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象 ...

  3. 四、java多线程核心技术——synchronized同步方法与synchronized同步快

    一.synchronized同步方法 论:"线程安全"与"非线程安全"是多线程的经典问题.synchronized()方法就是解决非线程安全的. 1.方法内的变 ...

  4. java多线程(二)——锁机制synchronized(同步方法)

    synchronized Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.当两个并发线程访问同一个对象object中 ...

  5. 深入理解使用synchronized同步方法和同步代码块的区别

    一.代码块和方法之间的区别 首先需要知道代码块和方法有什么区别: 构造器和方法块,构造器可以重载也就是说明在创建对象时可以按照不同的构造器来创建,那么构造器是属于对象,而代码块呢他是给所有的对象初始化 ...

  6. synchronized同步方法《一》

    1.方法内的变量为线程安全 "非线程安全"问题存在于"实例变量"中,如果是方法内部的私有变量,则不存在"非线程安全"问题,所得结果也就是&q ...

  7. 二十二 synchronized同步方法

    一 Synchronized锁: 1 synchronized取得的锁都是对象锁,而不是把一段代码或方法加锁. synchronized是给该方法的实例对象加锁.如果多个线程访问的是同一个对象  的s ...

  8. synchronized同步方法和同步代码块的区别

    同步方法默认使用this或者当前类做为锁. 同步代码块可以选择以什么来加锁,比同步方法更精确,我们可以选择只有会在同步发生同步问题的代码加锁,而并不是整个方法. 同步方法使用synchronized修 ...

  9. java synchronized静态同步方法与非静态同步方法,同步语句块

    摘自:http://topmanopensource.iteye.com/blog/1738178 进行多线程编程,同步控制是非常重要的,而同步控制就涉及到了锁. 对代码进行同步控制我们可以选择同步方 ...

随机推荐

  1. Boa服务器移植

    Boa是一种非常小巧的Web服务器,其可执行代码只有大约60KB左右.作为一种单任务Web服务器,Boa只能依次完成用户的请求,而不会fork出新的进程来处理并发连接请求.但Boa支持CGI,能够为C ...

  2. Windows下CURL扩展无效之终极解决办法。

    本地开发环境使用WAMP快速搭建,在使用PHP的CURL时可能会存在无法载入情况,这里提供终极解决方法. 1.在php.ini配置文件中启用 php_curll.dll 扩展: (环境已经自动附带 l ...

  3. Java变量初始化之后的默认值问题

    1) 局部变量初始化(局部变量:函数.语句中的变量,只在所属区域内有效)局部变量声明后,Java虚拟机不会自动给它初始化为默认值.因此对于局部变量,必须经过显示的初始化,才能使用它.如果使用一个没有被 ...

  4. P3165 [CQOI2014]排序机械臂

    题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到高度最低的物品的位置 P1P_1P1​ ,并把左起第一个物品至 P1P_1P1 ...

  5. hdu 5195 DZY Loves Topological Sorting (拓扑排序+线段树)

    DZY Loves Topological Sorting Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 ...

  6. Unity使用C#实现简单Scoket连接及服务端与客户端通讯

    简介: 网络编程是个很有意思的事情,偶然翻出来很久之前刚开始看Socket的时候写的一个实例,贴出来吧 Unity中实现简单的Socket连接,c#中提供了丰富的API,直接上代码. 服务端代码: [ ...

  7. 学习Spring Boot:(二)启动原理

    前言 主要了解前面的程序入口 @@SpringBootApplication 这个注解的结构. 正文 参考<SpringBoot揭秘 快速构建微服务体系>第三章的学习,总结下. Sprin ...

  8. Java之List和Set

    List.Set.数据结构.Collections 初次学习,涉及到List集合,Set集合和数据结构方面的一些知识,有错误还请批评指正 数据结构 数据存储的常用结构有:栈.队列.数组.链表和红黑树. ...

  9. EL与OGNL以及值栈的理解

    这里先添加下在项目遇到的问题: 这两天在做论坛项目的时候,犯了一个错误:将数据放入值栈中,结果jsp页面获取不到. 困扰了许久: 总结如下: (1)每个action对应相应页面的值栈中值的获取,在属于 ...

  10. Android am命令使用

    一.开启Activity.服务.广播 1.开启Activity.服务.广播基础知识 通过adb shell,可以使用activity manager(arm)工具来执行不同的系统操作,如开启一个act ...