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

最普遍的同步方式就是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. 【算法】超大数组去重(Java语言实现)

    要求时间复杂度和空间复杂度尽可能低! 情景一:需要返回的是不重复的数组. 情景二:只需要返回不重复的元素个数.

  2. FAQ:Python环境变量配置

    Python安装安装成,需要配置环境变量: 默认情况下,在windows下安装python之后,系统并不会自动添加相应的环境变量.此时不能在命令行直接使用python命令. 1. 首先需要在系统中注册 ...

  3. linux----定义命令别名

    1.定义命令别名的语法: alias nickName='command'#用于定义. unalias  nickName#用于撤消一个别名的定义. 如:alias cls='clear' 2.应该要 ...

  4. Android4: Write Storage权限问题

    原文:Android4: Write Storage权限问题 2.3中声明 <uses-permission android:name="android.permission.WRIT ...

  5. 历峰集团3.43亿美元收购Net-a-Porter剩余股权_财经_腾讯网

    历峰集团3.43亿美元收购Net-a-Porter剩余股权_财经_腾讯网 历峰集团3.43亿美元收购Net-a-Porter剩余股权

  6. Choosing Between ElasticSearch, MongoDB &amp; Hadoop

    An interesting trend has been developing in the IT landscape over the past few years.  Many new tech ...

  7. 开源点评:Protocol Buffers介绍

    今天来介绍一下“Protocol Buffers”(下面简称protobuf)这个玩意儿.本来俺在构思“生产者/消费者模式 ”系列的下一个帖子:关于生产者和消费者之间的传输数据格式.因为里面扯到了pr ...

  8. 得到client真IP住址

    1.引进的必要性log4j-1.2.14.jar package org.ydd.test; import java.util.Enumeration; import javax.servlet.ht ...

  9. Objective-C时间戳转换的转换和时间

    什么是时间戳? 时间戳(timestamp),一般是一个字符序列.唯一地标识某一刻的时间.数字时间戳技术是数字签名技术一种变种的应用. 思考:简单来讲就是依据文件hash加密后生成的摘要和时间生成的时 ...

  10. 关于Installation error: INSTALL_FAILED_MISSING_SHARED_LIBRARY

    google 给的android的例子里,有用到google APIs,新版本的ADT默认只有Android的SDK,如果运行平台与目标平台不一致会报:Installation error: INST ...