java concurrent之前戏synchronized
对于多线程共享资源的情况须要进行同步,以避免一个线程的修改被还有一个线程的修改所覆盖。
最普遍的同步方式就是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的更多相关文章
- Java并发编程:Synchronized及其实现原理
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- java concurrent包的学习(转)
java concurrent包的学习(转) http://my.oschina.net/adwangxiao/blog/110188 我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常 ...
- Java Concurrent Topics
To prevent Memory Consistency Errors(MCEs), it is good practice to specify synchronized class specif ...
- Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java中编写线程安全代码的原理(Java concurrent in practice的快速要点)
Java concurrent in practice是一本好书,不过太繁冗.本文主要简述第一部分的内容. 多线程 优势 与单线程相比,可以利用多核的能力; 可以方便的建模成一个线程处理一种任务; 与 ...
- Java并发编程:synchronized
Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...
- Java同步块(synchronized block)使用详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- java多线程——同步块synchronized详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- How to Create a Java Concurrent Program
In this Document Goal Solution Overview Steps in writing Java Concurrent Program Template ...
随机推荐
- struts2笔记07-action扩展名
1.action扩展名 默认扩展名可以去/org/apache/struts2/default.properties中查看 struts.action.extension=action,, defau ...
- oracle --- spoon
一.Spool常用的设置 set arraysize 5000; //此参数可提高SPOOL卸载的速度,最大可以设置为5000 set autotrace on; //设置允许对执行的sql进 ...
- IE WebDriver 因保护模式无法启动的解决 (转载)
现在Win7 已经应用很多了,即使是最原始的Win7 也是IE8,最新的patch后,都升到了IE11 Win7下预装高版本IE的情况下,启动IE WebDriver可能会出现: org.openqa ...
- JDBC笔记
简介 JDBC是Java规定的访问数据库的API,目前主流的数据库都支持JDBC.使用JDBC访问不同的数据库时需要安装不同的驱动. JDBC定义了数据库的链接,SQL语句的执行以及查询结果集的遍历等 ...
- Android 屏幕适配方式
适配:即当前应用在相同的手机上面显示相同的效果.适配前需要首先确定当前手机所属像素密度类型(如:xhdpi.hdpi.mdpi等) 像素密度:每英寸上分布的像素点个数,单位(dpi,ppi),利用勾股 ...
- 【译】浅谈微软OneNote的自动化测试工具
当我们向人们介绍OneNote的自动化时,有一个问题被相当频繁地提到,担忧我们的自动化框架中UI层面测试偏少. 我不喜欢基于UI的自动化.我知道在市场上有许多的自动化系统都是基于UI的自动化(点击按钮 ...
- HP DL360 G7通过iLO部署系统
HPDL360 G7通过iLO部署系统 HP DL360 G7是没有光驱的服务器,可使用USB外置光驱.PXE网络安装.ILO方式的安装操作系统 一.HP iLO 简介 iLO 是一组芯片,内部是vx ...
- 常用的连接字符串(vs中连接sqlserver)方便随时查看
Sql Server身份验证有两种,一种是Windows身份验证,还有一种是Sql Server 身份验证 Windows身份验证连接字符串: string connectionString = &q ...
- CentOS安装常用软件
下载第三方库rpmforge,找到合适自己版本的rpmforge下载,用以支持NTFS格式硬盘和MP3格式音频或其他 http://pkgs.repoforge.org/rpmforge-releas ...
- C#委托的简单剖析
为什么在Button1的Click事件发生之后,button1_Click方法就会被调用呢? 实际上,在我们双击Button1的时候,IDE自动的添加了一段代码,该段代码位于“Form1.Design ...