线程的同步之Synchronized的使用
线程的同步:一般的并发指的就是多个线程访问同一份资源。多个线程同时访问(修改)同一份资源的话,就会有可能造成资源数据有误。
如果多个线程访问多个不同资源,就不会造成线程同步。
如果要解决这个问题,就需要对线程使用同步存取。java中提供了一个synchronized关键字来对方法或者某个块加锁。从而达到锁定某个区域,不可
同时修改以免数据有误的情况。
synchronized关键字可以锁定的部分:
1、锁定方法:在方法上加入synchronized关键字就表明在使用该方法的时候需要获取相应的锁。
2、锁定块:锁定块的参数需要是对象,不可是基本类型数据
synchronized(引用类型变量 | this | 对象.class){
//逻辑代码
}

上图表示非同步线程和同步线程的比较,可以看出非同步的时候,线程1和线程2都是在同一个时间段访问同一个transter方法,而使用了同步之后,线程2如果想调用transter方法就必须等待线程1调用完成后才可执行。
这里以12306抢票代码为例来说明线程同步的synchronized关键字的使用。
1、未使用synchronized锁的情况
首先来看未使用synchronized的情况会是什么样?
抢票的线程代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Web12306 implements Runnable{ private int num=10;//总共10张票 private boolean flag = true; @Override public void run() { while(flag){ //黄牛抢到了3 农民工抢到了1 黄牛抢到了0 程序员抢到了-1 test1();//线程不安全,数据不准确:结果有-1值 } } //1、线程不安全 public void test1(){ if (num<=0) { flag = false; return;//跳出循环,结束 } try { Thread.sleep(500);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }} |
测试线程代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class SynDemo1 { public static void main(String[] args) { //真实角色 Web12306 web = new Web12306(); //代理角色 Thread proxy1 = new Thread(web,"黄牛"); Thread proxy2 = new Thread(web,"程序员"); Thread proxy3 = new Thread(web,"农民工"); proxy1.start(); proxy2.start(); proxy3.start(); }} |
测试结果如下:可以看出最后的结果会出现0和-1这样错误的数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
黄牛抢到了10农民工抢到了8程序员抢到了9黄牛抢到了7农民工抢到了6程序员抢到了5黄牛抢到了4农民工抢到了3程序员抢到了2黄牛抢到了1农民工抢到了0程序员抢到了-1 |
为什么会出现这样的数据呢?
因为现在三个线程都启动了,都是在运行状态中访问test1方法,修改其中的num值。因为他们三个会同时都会进入该方法的情况,所以修改的数据也会出现当:黄牛抢走了1,这时候农民工和程序员还在test1方法里,他俩也会对num进行--操作。所以,最后的结果就是0和-1
2、使用synchronized关键字锁定方法:
线程修改抢票代码,在test1方法上加入synchronized关键字,使该方法锁定。调用时需要先获取锁(线程安全)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Web12306 implements Runnable{ private int num=10;//总共10张票 private boolean flag = true; @Override public void run() { while(flag){ test2();//线程安全,数据准确 } } //2、方法锁:加上synchronized表示线程安全的 public synchronized void test2(){ if (num<=0) { flag = false; return;//跳出循环,结束 } try { Thread.sleep(500);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }} |
继续使用上面的main方法测试,测试结果如下:抢票结果正确没问题。
|
1
2
3
4
5
6
7
8
9
10
|
黄牛抢到了10黄牛抢到了9黄牛抢到了8黄牛抢到了7黄牛抢到了6黄牛抢到了5黄牛抢到了4黄牛抢到了3黄牛抢到了2农民工抢到了1 |
3、使用synchronized锁定代码块:锁定当前对象
继续修改抢票代码,在方法内部使用synchronized锁定块
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Web12306 implements Runnable{ private int num=10;//总共10张票 private boolean flag = true; @Override public void run() { while(flag){ test3();//线程安全,数据准确 } } //3、锁定块:当前对象也就是Web12306 public void test3(){ synchronized(this){//锁定当前对象 if (num<=0) { flag = false; return;//跳出循环,结束 } try { Thread.sleep(500);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } }} |
继续使用上面的main方法测试,测试结果如下:抢票结果正确没问题。
|
1
2
3
4
5
6
7
8
9
10
|
黄牛抢到了10黄牛抢到了9黄牛抢到了8黄牛抢到了7黄牛抢到了6黄牛抢到了5黄牛抢到了4黄牛抢到了3黄牛抢到了2农民工抢到了1 |
4、使用synchronized锁定代码块:锁定部分代码块
可以看出test3方法是使用synchronized关键字锁定了整个方法区域。那如果就只锁定一部分呢?这里假如只锁定if(num<=0)这个判断部分
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Web12306 implements Runnable{ private int num=10;//总共10张票 private boolean flag = true; @Override public void run() { while(flag){ test4();//线程不安全,数据不准确:出现-1 【锁定范围不正确】 } } //4、使用synchronized锁定部分资源 public void test4(){ synchronized(this){ if (num<=0) { flag = false; return;//跳出循环,结束 } }//只锁定到此 try { Thread.sleep(500);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }} |
使用main方法测试结果如下:可以看出最后的结果同样也会出现0和-1这样错误的数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
黄牛抢到了10农民工抢到了8程序员抢到了9黄牛抢到了7农民工抢到了6程序员抢到了5黄牛抢到了4农民工抢到了3程序员抢到了2黄牛抢到了1农民工抢到了0程序员抢到了-1 |
分析下为什么会出现这样的结果?我们知道test4中只锁定了if这部分。假设现在程序num现在等于1
1.此时线程A,B,C三个线程都会进入到12行,if判断的部分。A先进来拿到了锁,判断此时num=1 。然后释放锁走到18行,try的部分
2.线程A在18行try部分并没有对num--操作。此时线程B也进入到了12行拿到了锁。也到了18行。现在18行是A,B两个线程。A往下执行拿走了num
等线程B再去拿num的时候,num已经等于0了。
3.同理,C再去拿num的时候num已经是0-1 = -1了。
5、使用synchronized锁定部分资源:只锁定num变量
由于synchronized的参数需要是对象,所以把基本类型包装成引用类型
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Web12306 implements Runnable{ private int num=10;//总共10张票 private boolean flag = true; @Override public void run() { while(flag){ test5();//线程不安全,数据不准确:出现重复数据【锁定范围不正确】 } } //5、使用synchronized锁定部分资源:锁定num变量 public void test5(){ synchronized((Integer)num){ if (num<=0) { flag = false; return;//跳出循环,结束 } } try { Thread.sleep(500);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }} |
使用main方法测试结果如下:可以看出最后的结果会出现重复数据(两个6)锁定资源不正确也是线程不安全的
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
黄牛抢到了10农民工抢到了9程序员抢到了8农民工抢到了7黄牛抢到了6程序员抢到了6黄牛抢到了5农民工抢到了4程序员抢到了3黄牛抢到了2程序员抢到了1农民工抢到了0黄牛抢到了-1 |
1、synchronized关键字表示锁,可以加在方法上或者一个代码块中
synchronized(引用类型变量 | this | 对象.class){
//需要锁的区域
}
2、不加synchronized关键字的方法是线程不安全的
加了synchronized表示线程安全,线程安全的话会降低效率。因为共享的资源被加了锁,会有锁等待时间
3、在加synchronized代码块的时候需要注意,注意锁的范围。
范围太大----->会降低效率。范围太小------>线程不安全
线程的同步之Synchronized的使用的更多相关文章
- 线程的同步之Synchronized在单例模式中的应用
synchronized在单例模式中的使用 在单例模式中有一种懒汉式的单例,就是类初始化的时候不创建对象.等第一次获取的时候再创建对象.这种单例在单线程下是没有问题的获取的也都是同一个对象.但是如果放 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...
- Java多线程之线程同步【synchronized、Lock、volatitle】
线程同步 线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多. ...
- Java线程:线程的同步-同步方法
Java线程:线程的同步-同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段. 线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问 ...
- Java线程:线程的同步与锁
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...
- java线程(2)--同步和锁
参考转载:http://rainyear.iteye.com/blog/1734311 http://turandot.iteye.com/blog/1704027 http://www.cnblog ...
- java 线程数据同步
java 线程数据同步 由买票实例 //java线程实例 //线程数据同步 //卖票问题 //避免重复卖票 //线程 class xc1 implements Runnable{ //定义为静态,可以 ...
随机推荐
- mysql 数据操作 单表查询 简单查询 避免重复DISTINCT
创建数据库company create database company charset=utf8; use company; company.employee 员工id id int 姓名 emp_ ...
- rest-framework框架的基本组件
快速实例 Quickstart 大致步骤 (1)创建表,数据迁移 (2)创建表序列化类BookSerializer class BookSerializer(serializers.Hyperlink ...
- 自定义 Repository 方法
为某一个 Repository 上添加自定义方法 步骤: 定义一个接口: 声明要添加的, 并自实现的方法 提供该接口的实现类: 类名需在要声明的 Repository 后添加 Impl, 并实现方法 ...
- (16)Cocos2d-x 多分辨率适配完全解析
Overview 从Cocos2d-x 2.0.4开始,Cocos2d-x提出了自己的多分辨率支持方案,废弃了之前的retina相关设置接口,提出了design resolution概念. 3.0中有 ...
- cocos代码研究(21)Widget子类Text,TextAtlas,TextBMFont学习笔记
理论基础 Text类又称ttf格式文本,可以用ttf文件或者系统自带字体,支持文字多,但是ttf文件格式体积大,渲染速度慢: TextBMFont类又称fnt格式文本,纹理创建,根据纹理上有的文字来显 ...
- jQuery中的prop和attr区别
最近在做一个项目用jq时发现一个问题 在谷歌中可以正常出效果 但是在火狐中就是不行 就是这个prop和attr 之前用的是attr方法 但是在火狐中不出效果 于是特意看了两者的区别 主要 ...
- Python Missing parentheses in call to 'print'
原来是因为Python2.X和Python3.X不兼容. 我安装的是Python3.X,但是我试图运行的却是Python2.X 的代码. 所以上面的语法在python3中是错误的.在python3中, ...
- Solr DIH query 工作流
本文地址 http://www.cnblogs.com/jasonxuli/p/6491270.html DataImportHandler (DIH) 支持全量数据导入和增量数据导入,主要有四个qu ...
- 20145319 《网络对抗》逆向与Bof基础
20145319 逆向与Bof实验 1 实验内容 本次实验以可执行文件pwn1为例,将对pwn1进行反汇编的基础上进行功能上的解读,并进行缓冲区溢出攻击 可执行文件pwn1的正常流程是主函数调用foo ...
- linux下 ip指令
目录 Network ip command Command :ip 简介 内容 Network ip command Command :ip 简介 ip 是個指令喔!並不是那個 TCP/IP 的 IP ...