对于多线程共享资源的情况须要进行同步,以避免一个线程的修改被还有一个线程的修改所覆盖。

最普遍的同步方式就是synchronized。

把代码声明为synchronized。有两个重要后果,一般是指该代码具有 原子性(atomicity)和 可见性(visibility)。

1、原子性强调的是运行。意味着个时刻,仅仅有一个线程可以运行一段代码,这段代码通过一个monitor object保护。从而防止多个线程在更新共享状态时相互冲突。

2、可见性强调的是结果。它要对付内存缓存和编译器优化的各种反常行为。它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的还有一个线程是可见的。

同步方法

看一个样例就明确了:

import java.util.Random;

public class TestSyncClass {

 private int num=0;

 

 private Random random=new Random();

 

 public synchronized void testAdd1(){

  System.out.println("testAdd1--->>");

  num++;

  try {

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   e.printStackTrace();

  }

  System.out.println("1-result-->>"+num);

 }

 

 public synchronized void testAdd2(){

  System.out.println("testAdd2--->>");

  num++;

  try {

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   e.printStackTrace();

  }

  System.out.println("2-result-->>"+num);

 }

 

 public  void testAdd3(){

  System.out.println("testAdd3--->>");

  num++;

  try {

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   e.printStackTrace();

  }

  System.out.println("3-result-->>"+num);

 }

 

 public static void main(String[] args) {

  final TestSyncClass syncClass=new TestSyncClass();

  Thread thread1=new Thread(){

   @Override

   public void run() {

    syncClass.testAdd1();

    super.run();

   }

  };

  

  Thread thread2=new Thread(){

   @Override

   public void run() {

    syncClass.testAdd2();

    super.run();

   }

  };

  

  Thread thread3=new Thread(){

   @Override

   public void run() {

    syncClass.testAdd3();

    super.run();

   }

  };

  

  thread1.start();

  thread2.start();

  thread3.start();

 }

}

代码执行结果:

testAdd1--->>

testAdd3--->>

1-result-->>2

3-result-->>2

testAdd2--->>

2-result-->>3

代码中testAdd1、testAdd2是被synchronized声明的方法。testAdd3没有声明。在执行的时候因为testAdd3没有被声明,所以在紧跟着開始执行testAdd1的时候也执行了testAdd3。结果testAdd1执行的结果被testAdd3的结果覆盖了,打印了同样的值3。这个主要是可见性的问题。因为testAdd2也是被声明过的,所以testAdd2并没有马上执行。而是等testAdd1执行完之后才開始执行。

全部对象都自己主动含有单一的锁(也称为监视器monitor object)。

当在对象上调用其随意synchronized方法的时候,此对象都被加锁。这时该对象上的其它synchronized方法仅仅有等到前一个方法调用完成并释放了锁之后才干被调用。

针对每一个类,也有一个锁(作为类的Class对象的一部分)。所以synchronized static 方法能够在类的范围内防止对static数据的并发訪问。

同步块

不管何种情况,要想锁住代码,必须使用同样的锁。比如把testAdd2改成

        private Object object=new Object();

 public void testAdd2(){

  synchronized(object){

   System.out.println("testAdd2--->>");

   num++;

   try {

    Thread.sleep(1000);

   } catch (InterruptedException e) {

    e.printStackTrace();

   }

   System.out.println("2-result-->>"+num);

  }

 }

则testAdd2和testAdd1就不会相互等待了。结果例如以下:

testAdd2--->>

testAdd3--->>

testAdd1--->>

3-result-->>3

2-result-->>3

1-result-->>3

事实上synchronized(object)是更安全的上锁方式。由于直接声明方法的形式用的是类的锁,而声明代码块的形式用的是私有属性的锁,尤其是在server开发的时候,外面类的锁非常easy被黑客获取。从而获取了攻击server的入口,而私有属性的私有性让黑客难以获取,所以它的锁就相对安全的多。

类同步

上面的main方法的三个线程用的是同一个TestSyncClass syncClass对象。假设每一个线程都各自创建一个对象就不能达到锁定代码的目标了。要想达到同步的目的,代码须要改动成例如以下:

import java.util.Random;

public class TestSyncClass {

 private int num = 0;

private static Object object = new Object();

private Random random = new Random();

public void testAdd1() {

  synchronized (object) {

   System.out.println("testAdd1--->>");

   num++;

   try {

    Thread.sleep(1000);

   } catch (InterruptedException e) {

    e.printStackTrace();

   }

   System.out.println("1-result-->>" + num);

  }

 }

public void testAdd2() {

  synchronized (object) {

   System.out.println("testAdd2--->>");

   num++;

   try {

    Thread.sleep(1000);

   } catch (InterruptedException e) {

    e.printStackTrace();

   }

   System.out.println("2-result-->>" + num);

  }

 }

public void testAdd3() {

  System.out.println("testAdd3--->>");

  num++;

  try {

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   e.printStackTrace();

  }

  System.out.println("3-result-->>" + num);

 }

public static void main(String[] args) {

  Thread thread1 = new Thread() {

   @Override

   public void run() {

    TestSyncClass syncClass = new TestSyncClass();

    syncClass.testAdd1();

    super.run();

   }

  };

Thread thread2 = new Thread() {

   @Override

   public void run() {

    TestSyncClass syncClass = new TestSyncClass();

    syncClass.testAdd2();

    super.run();

   }

  };

Thread thread3 = new Thread() {

   @Override

   public void run() {

    TestSyncClass syncClass = new TestSyncClass();

    syncClass.testAdd3();

    super.run();

   }

  };

thread1.start();

  thread2.start();

  thread3.start();

 }

}

执行结果:

testAdd1--->>

testAdd3--->>

3-result-->>1

1-result-->>1

testAdd2--->>

2-result-->>1

事实上使用synchronized (TestSyncClass.class)类的锁也能达到类似的效果,可是考虑到私有属性的安全性就直接使用上面代码做实例了。

注意:synchronized是不能继承的,父类中synchronized的声明在子类的继承过程中须要再次声明,否则synchronized将会丢失。

wait(), notify()。notifyAll()

基类不光有锁。还有这三个方法。wait()会让获取锁的线程等待并释放锁,直到notify()或notifyAll()唤醒并又一次获取锁。

先看一个样例:

public class TestSyncClass {

 private int num = 0;

private Object object = new Object();

private Object object1 = new Object();

public  void testAdd(int index) {

  System.out.println("testAdd" + index + "--->>");

  synchronized (object) {

   num++;

   try {

    object.wait();

   } catch (InterruptedException e) {

    e.printStackTrace();

   }

   System.out.println(index + "-result-->>" + num);

  }

 }

public void release() {

  synchronized (object) {

   object.notifyAll();

   System.out.println("-release-->>");

  }

 }

public static void main(String[] args) {

  final TestSyncClass syncClass = new TestSyncClass();

  Thread thread1 = new Thread() {

   @Override

   public void run() {

    syncClass.testAdd(1);

    super.run();

   }

  };

Thread thread2 = new Thread() {

   @Override

   public void run() {

    syncClass.testAdd(2);

    super.run();

   }

  };

Thread thread3 = new Thread() {

   @Override

   public void run() {

    syncClass.testAdd(3);

    super.run();

   }

  };

thread1.start();

  thread2.start();

  thread3.start();

Thread thread4 = new Thread() {

   @Override

   public void run() {

    try {

     Thread.sleep(2000);

    } catch (InterruptedException e) {

     e.printStackTrace();

    }

    syncClass.release();

    super.run();

   }

  };

  thread4.start();

 }

}

执行结果:

testAdd1--->>

testAdd2--->>

testAdd3--->>

-release-->>

3-result-->>3

2-result-->>3

1-result-->>3

调用object的wait(), notify()。notifyAll()法前,必须获得object锁,也就是这三个方法必须写在synchronized(obj) {…} 代码段内。否则跑出异常java.lang.IllegalMonitorStateException。

调用object.wait()后。线程A就释放了object的锁,否则syncClass.release()无法获得object锁,等待的线程。

当object.wait()方法返回后。各个线程须要再次获得object锁,才干继续运行。

notify()仅仅能唤醒线程,notifyAll()则能所有唤醒,可是个线程须要又一次竞争object的锁。

java concurrent之前戏synchronized的更多相关文章

  1. Java并发编程:Synchronized及其实现原理

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  2. java concurrent包的学习(转)

    java concurrent包的学习(转) http://my.oschina.net/adwangxiao/blog/110188 我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常 ...

  3. Java Concurrent Topics

    To prevent Memory Consistency Errors(MCEs), it is good practice to specify synchronized class specif ...

  4. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  5. Java中编写线程安全代码的原理(Java concurrent in practice的快速要点)

    Java concurrent in practice是一本好书,不过太繁冗.本文主要简述第一部分的内容. 多线程 优势 与单线程相比,可以利用多核的能力; 可以方便的建模成一个线程处理一种任务; 与 ...

  6. Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  7. Java同步块(synchronized block)使用详解

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

  8. java多线程——同步块synchronized详解

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

  9. How to Create a Java Concurrent Program

    In this Document   Goal   Solution   Overview   Steps in writing Java Concurrent Program   Template ...

随机推荐

  1. WEB开发之如何改善PHP开发方式

    改善PHP开发方式一般可以分为以下几种实现方式: 1.组织和样式     找出一种适合你的组织方法和编码样式,并且一直坚持下去,这样的话,你的代码的组织和布局会变得十分有条理.我们不应该轻视代码的组织 ...

  2. win7_32位安装MySQL_5.6以及密码修改方法

    1.下载mysql: http://www.xiazaiba.com/html/361.html 2.安装 方便起见,全部默认下一步吧,原理一个样,最后安装到: 3.配置环境变量 我这里添加的是  C ...

  3. TLC是什么

    TLC = Triple-Level Cell,即3bit/cell,它的寿命短,速度慢,约500-5000次擦写寿命. 现在U盘多为MLC,TLC也有一部分,将来TLC会占大部分市场. 一种名为TL ...

  4. poj1656---数黑格子

    题意:有white,black,test操作 black将给定范围涂黑,white将给定范围涂白,test将给定范围的黑格子数出来并且输出 思路:无论哪个操作格子范围都在  (x,y)  (x+L-1 ...

  5. DRP总结

    DRP终于结束了,战线有点长了.记得刚开始听说DRP的时候,感觉这个名词很专业,再加上视频一共有300集,顿时感觉这是一个大项目,很正规.很专业的项目.虽然后来知道DRP知识ERP的一个分支,项目规模 ...

  6. [置顶] Oracle GoldenGate 常见问题:长事务处理

    长事务的影响 OGG是基于事务级的实时复制工具,也就是说OGG只复制已提交的事务,在遇到事务的commit或rollback之前,它会将每个事务的操作存储在称为cache的托管虚拟内存池中.内存再大也 ...

  7. 浅谈POSIX线程的私有数据

    当线程中的一个函数需要创建私有数据时,该私有数据在对函数的调用之间保持一致,数据能静态地分配在存储器中,当我们采用命名范围也许可以实现它使用在函数或是文件(静态),或是全局(EXTERN).但是当涉及 ...

  8. Objective-C中的@dynamic

    一.@dynamic与@synthesize的区别 @property有两个对应的词,一个是@synthesize,一个是@dynamic.如果@synthesize和@dynamic都没写,那么默认 ...

  9. Linux下文件及目录的一些操作(附递归遍历目录源码)

    1.获取当前工作目录 #include <unistd.h> 1.char *getcwd(char *buf,size_t size); 2. 3.其中,buf为缓冲区地址,size为给 ...

  10. jQuery Mobile基础

    1.安装 在<head></head>标签里边写入以下内容 jQuery Mobile CDN: <head> <meta charset="utf ...