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

最普遍的同步方式就是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. Nodejs学习笔记——Assert(断言)

    Assert - a:actual e:expected m:message o:operator v:value b:block assert.fail(a, e, m, o) assert(v, ...

  2. Office 2010 Toolkit and EZ-Activator

    “Office 2010 Toolkit 2.0.1”是“迷你KMS”的更新换代版本.虽然是单一可执行程序,但一身承担两大职能:“KMS服务器”和“客户激活端”.“Office 2010 Toolki ...

  3. CSharp tar类型文件压缩与解压

    最近闲暇时间开始写点通用基础类在写到tar类型文件压缩与解压时遇到点问题 压缩用的类库我是下载的 SharpZipLib_0860版本 先上代码 加压核心 /// <summary> // ...

  4. NOI2011 Day1

    NOI2011 Day1 兔农 题目描述:\(fib[1]=fib[2]=1, fib[i]=fib[i-2]+fib[i-1] (i\geq 3)\),若\(fib[i] \equiv 1(mod ...

  5. poj2405---体积几何

    #include <stdio.h> #include <stdlib.h> #include<math.h> #define pi acos(-1) int ma ...

  6. Puppet的执行过程

    图1 Puppet工作流程 1. 客户端Puppetd向Master发起认证请求,或使用带签名的证书. 2. Master告诉Client你是合法的. 3. 客户端Puppetd调用Facter,Fa ...

  7. collection系列用法-defaultdict()

    defaultdict() 定义以及作用 返回一个和dictionary类似的对象,和dict不同主要体现在2个方面: 可以指定key对应的value的类型. 不必为默认值担心,换句话说就是不必担心有 ...

  8. Android SQLite 事务处理

    应用程序初始化时需要批量的向sqlite中插入大量数据,单独的使用for+Insert方法导致应用响应缓慢,因为 sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作.我 ...

  9. Nodejs解析HTML网页模块 jsdom

    工作需要抓取某些网页,所以今天试用下了node下的jsdom模块.同样功能的还有jquery jsdom https://npmjs.org/package/jsdom API很简单. jsdom.e ...

  10. JavaScript引用类型之Array数组之强大的splice()方法

    splice()方法可以说是Array数组最强大的方法,他的用法很多,主要用法是向数组的中部插入项! 下面是它的用法: arrayObject.splice(index,howmany,element ...