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

最普遍的同步方式就是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. Javascript 匀速运动停止条件——逐行分析代码,让你轻松了解运动的原理

    我们先来看下之前的匀速运动的代码,修改了速度speed后会出现怎么样的一个bug.这里加了两个标杆用于测试 <style type="text/css"> #div1 ...

  2. SQLSERVER 使用WITH函数查找时间点最大数据行

      --表结构及数据: DTIME TYPE MONEY 2015-10-14 13:50:35.000 shopping 20 2015-10-21 13:51:24.000 shopping 40 ...

  3. Eclipse下如何打开ftl文件

    ftl文件是freemarker模板文件,用freemarker时,常用该文件模板:但是该文件在eclipse编辑时,黑白底的,没有任何提示,下面介绍如何用JSP编辑器打开该文件. 工具/原料   e ...

  4. Windows平台内核级文件访问

    1.背景     在windows平台下,应用程序通常使用API函数来进行文件访问,创建,打开,读写文件.从kernel32的CreateFile/ReadFile/WriteFile函数,到本地系统 ...

  5. rsyslog masg和rawmsg的区别

    msg the MSG part of the message (aka "the message" ;)) message 的MSG 部分 rawmsg the message ...

  6. 值得赞扬的尝试与进步——CSDN开源夏令营第一印象

    注:写这篇文章时我并未參加CSDN开源夏令营,也不确定是否会參加以及是否能參加上. 欣闻CSDN举办了"CSDN开源夏令营"活动.第一感觉是CSDN作为活动的组织者是很值得称赞的. ...

  7. Leetcode:Swap Nodes in Pairs 单链表相邻两节点逆置

    Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2-& ...

  8. OS笔记047代理传值和block传值

    在两个不同的控制器之间传递数据,可以使用代理传值或者block传值. 例子是一个简单通讯录. 主界面如下: 添加联系人界面 查看/编辑联系人界面:默认是查看模式,点击编辑后进入编辑模式 编辑模式 数据 ...

  9. CTabCtrl - 如何使用TabCtrl控件

    CTabCtrl - 如何使用TabCtrl控件 建立一个基于对话框的MFC应用程序TabCtrlTest Step 1:在资源视图中,新建对话框资源,资源ID:IDD_TabCtrlExample_ ...

  10. GridView边线Border设置

    1.黑色实线:(行列都有) <asp:GridViewID="GridView1"runat="server"CellPadding="3&qu ...