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

最普遍的同步方式就是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. easyui datagrid 列的内容超出所定义的列宽时,自动换行

    定义表单  nowrap="false"可以使得列中的内容超出所定义的列宽是就会自动换行pagination : true, // 当true时在DataGrid底部显示一个分页工 ...

  2. ALOS卫星介绍

    ALOS卫星介绍 作者:ALOS    文章来源:ALOS    点击数:    更新时间:2013-6-21 摘要:日本地球观测卫星计划主要包括2个系列:大气和海洋观测系列以及陆地观测系列.先进对地 ...

  3. 雕爷牛腩这样判断零售未来-搜狐IT

    雕爷牛腩这样判断零售未来-搜狐IT 雕爷牛腩这样判断零售未来

  4. 查找——图文翔解Treap(树堆)

    之前我们讲到二叉搜索树,从二叉搜索树到2-3树到红黑树到B-树. 二叉搜索树的主要问题就是其结构与数据相关,树的深度可能会非常大,Treap树就是一种解决二叉搜索树可能深度过大的还有一种数据结构. T ...

  5. HTML系列(四):编辑图像

    一.图像的基本概念 1.矢量图:文件占用空间小,放大后图像不会失真,和分辨率无关.适用于图形设计.文字设计.标志设计.版式设计等. 2.位图:由像素点组成,文件较大,放大和缩小图像会失真. 3.有损压 ...

  6. C# 颜色有3种表示方式: 6位16进制、RGB、 颜色关键字

    最常用的是6位16进制的代码表示法.如bgcolor=#ff0000;其中#只是表示使用6位16进制的颜色代码声明颜色.代码的头两位即ff表示三原色中的红色,范围当然是16进制的00-ff,中间两位即 ...

  7. C#中的ref与out参数(本文仅作为个人笔记)

    假如一个方法的参数(形参)是引用类型,那么使用那个参数来进行的任何修改都会改变参数引用的数据.但这里的关键在于,虽然引用的数据发生了变化,但参数本生没有改变——它仍然引用的是一个对象.换句话说,虽然可 ...

  8. 善于 调用Windows API

    前一段时间看见别人做的一个自动填写信息并且点击登录的程序,觉得很有意思. 其实就是在程序中调用Windows的API,那么如何调用,下面就做个简单的介绍. 写的简单粗暴, 不喜轻喷. 0.首先引入名称 ...

  9. hadoop之hdfs学习

    简介 HDFS(Hadoop Distributed File System )Hadoop分布式文件系统.是根据google发表的论文翻版的.论文为GFS(Google File System)Go ...

  10. P - Shopaholic

    P - Shopaholic Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit ...