一 同步的概念

  线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

  例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。

  MyRunnable.java

 package Thread;
public class MyRunnable implements Runnable{
private Foo foo=new Foo();
public static void main(String[] args){
MyRunnable r=new MyRunnable();
Thread ta=new Thread(r,"Thread-A");
Thread tb=new Thread(r,"Thread-B");
ta.start();
tb.start();
}
public void run(){
for(int i=0;i<3;i++){
this.fix(30);
try{
Thread.sleep(1);
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":当前foo对象的值="+foo.getX());
}
}
public int fix(int y){
return foo.fix(y);
}
}

  Foo.java

 package Thread;
public class Foo {
private int x=100;
public int getX(){
return x;
}
public int fix(int y){
x=x-y;
return x;
}
}

  运行结果:

 Thread-A:当前foo对象的值=40
Thread-B:当前foo对象的值=40
Thread-A:当前foo对象的值=-20
Thread-B:当前foo对象的值=-50
Thread-A:当前foo对象的值=-80
Thread-B:当前foo对象的值=-80

从结果看出,这样的输出值明显不合理。原因是两个线程不加控制的访问Foo对象并修改器数据所致。因此,应该对Foo的访问加以限制,每次只能有一个线程在访问。

1 、同步方法

  线程的同步是保证多线程安全访问竞争资源的一种手段。 对于同步具体的Java代码中需要完成两个操作:

  1、把竞争访问的资源标识为private;

  2、同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。(对于synchronized而言,只能标记非抽象方法,不能标识成员变量。)

  为了演示同步方法的使用,构建了一个信用卡账户,起初信用额度为100w,然后模拟透支、存款等操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户余额设为私有变量,禁止直接访问。

  BankTest.java

 package Thread;

 public class BankTest {
public static void main(String[] args){
User u=new User("小二",100);
MyThread t1=new MyThread("线程1",u,20);
MyThread t2=new MyThread("线程2",u,-60);
MyThread t3=new MyThread("线程3",u,-80);
MyThread t4=new MyThread("线程4",u,-30);
MyThread t5=new MyThread("线程5",u,32);
MyThread t6=new MyThread("线程6",u,21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread{
private User u;
private int y=0;
MyThread(String name,User u,int y){
super(name);
this.u=u;
this.y=y;
}
public void run(){
u.oper(y);
}
}
class User{
private String code;
private int cash;
User(String code,int cash){
this.code=code;
this.cash=cash;
}
public String getCode(){
return code;
}
/**
* 业务方法
* @param x 添加x万元
* */
public synchronized void oper(int x){
try{
Thread.sleep(10L);
this.cash+=x;
System.out.println(Thread.currentThread().getName()+"运行结果,增加"+x+",当前余额为:"+cash);
Thread.sleep(10L);
}
catch(InterruptedException e){
e.printStackTrace();
}
}
public String toString(){
return "User{" + "code=" + code + ",cash=" + cash +'}';
}
}

结果为:

 线程1运行结果,增加20,当前余额为:120
线程2运行结果,增加-60,当前余额为:60
线程6运行结果,增加21,当前余额为:81
线程5运行结果,增加32,当前余额为:113
线程4运行结果,增加-30,当前余额为:83
线程3运行结果,增加-80,当前余额为:3

但是如果把同步关键字synchronized去掉,结果为:

 线程2运行结果,增加-60,当前余额为:60
线程6运行结果,增加21,当前余额为:1
线程4运行结果,增加-30,当前余额为:3
线程5运行结果,增加32,当前余额为:3
线程1运行结果,增加20,当前余额为:60
线程3运行结果,增加-80,当前余额为:-20

显然是错误的,多个线程并发访问了竞争资源u,并对u的属性做了修改。可见同步的重要性。

2、同步块

  有时候,同步块比同步方法有更好的效果。在上个例子的基础上对oper方法做了改变,由同步方法改为同步块模式。

   BankTest.java

 public void oper(int x){
try{
Thread.sleep(10L);
synchronized(this){
this.cash+=x;
System.out.println(Thread.currentThread().getName()+"运行结果,增加"+x+",当前余额为:"+cash);
}
Thread.sleep(10L);
}
catch(InterruptedException e){
e.printStackTrace();
}
}

结果和上例是一样的。具体的变化思想为:

1 public syncronized int getX(){
2 return x++; //同步方法
3 }
4 与
5 public int getX(){
6 synchronized(this){
7 return x;//非同步方法
8 }
9 }

注意:

  在使用synchronized时,应该避免在synchronized方法或synchronized块中使用sleep或yield方法。因为synchronized程序块占用着对象锁,你休息那么其他线程只能等待。这样效率不高。同样,在同步程序块内调用yield方法让出CPU资源也没有意义,因为你占用着锁,其他资源无法访问。

二、静态方法同步

  要同步静态方法,需要一个用整个类对象的锁,这个对象就是这个类(xxx.class),如:

1 public static synchronized int setName(String name){
2 xxx.name=name;
3 }
4 等价于
5 public static int setName(){
6 synchronized(xxx.class){
7 xxx.name=name;
8 }
9 }

三、何时需要同步

  1、多个线程同时访问互斥(可交换)数据时,应该同步保护数据,确保两个线程不会同时更改它。

  2、非静态字段中可更改的数据,通常用非静态方法访问。

  3、静态字段中可更改的数据,用静态方法访问。

四、总结

  1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。

  2、线程同步方法是通过锁来实现,每个对象都仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程无法再访问该对象的其他同步方法。

  3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁不干预。一个线程获得锁,当在一个同步方法中访问另外对象的同步方法时,会获取两个对象的锁。

  4、对于同步,要时刻清醒哪个对象上同步,这是关键。

Java线程:同步的更多相关文章

  1. java 线程同步 原理 sleep和wait区别

    java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...

  2. Java线程同步_1

    Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...

  3. Java线程同步之一--AQS

    Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...

  4. java线程 同步临界区:thinking in java4 21.3.5

    java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...

  5. JAVA - 线程同步和线程调度的相关方法

    JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...

  6. Java线程同步的四种方式详解(建议收藏)

    ​ Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...

  7. Java线程同步和线程通信

    一.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步. 不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全. 1.同步 ...

  8. 【总结】Java线程同步机制深刻阐述

    原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...

  9. Java线程同步的方式

     java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),      将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的 ...

  10. Java线程同步(synchronized)——卖票问题

    卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...

随机推荐

  1. Python3基础 定义无参数无返回值函数 调用会输出hello world的函数

    镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...

  2. 深入理解setTimeout的作用域

    看了一篇关于setTimeout作用域的问题,其实之前在<javascript高级程序设计>时也看到了,分享给大家: 先总结下: 一.setTimeout中的延迟执行代码中的this永远都 ...

  3. BNU OJ 51003 BQG's Confusing Sequence

    二进制++高精度取模 #include<cstdio> #include<cstring> #include<algorithm> using namespace ...

  4. ASP.NET MVC 5 WEB API 用户验证

    参考博客:ASP.NET MVC5+EF6+EasyUI 后台管理系统(65)-MVC WebApi 用户验证 (1) 参考博客:MVC WebApi 用户验证 (2)构建ASP.NET MVC5+E ...

  5. 使用bootstrap建立响应式网页——头部导航栏

    1.要建立响应式网站的布局,这个时候bootstrap的高级就能体现出来了. 2.先来了解一下bootstrap提供了哪些响应式工具供我们使用: (1)屏幕宽度尺寸的概念: <768px     ...

  6. windows下egret环境搭建

    作者:zccst 2,下载安装WebStorm(经了解是西欧捷克开发的,欧洲人对审美不够,所以界面不太好看,但是功能很强大) (1)遇到的问题是注册码问题,不过也很容易在网上找到. (2)如何打开已创 ...

  7. DMLC深度机器学习框架MXNet的编译安装

    这篇文章将介绍MXNet的编译安装. MXNet的编译安装分为两步: 首先,从C++源码编译共享库(libmxnet.so for linux,libmxnet.dylib for osx,libmx ...

  8. linux下文件和目录的颜色表示

    蓝色表示目录: 绿色表示可执行文件: 红色表示压缩文件: 浅蓝色表示链接文件: 灰色表示其它文件: 红色闪烁表示链接的文件有问题了: 黄色是设备文件,包括block, char, fifo. (摘自: ...

  9. DNS服务器搭建(主、从、缓)

    主dns服务器搭建 在本机上搭建一个管理hngd.com域名的域名服务器1. 确保安装好以下bind域名服务器 [root@主人 ~]# rpm -qa |grep ^bindbind-chroot- ...

  10. [Unity Shader]Shader分类

    Shader的分类: Shader按管线分类一般分为固定渲染管线与可编程渲染管线 (1)固定渲染管线 ——这是标准的几何&光照(Transforming&Lighting)管线,功能是 ...