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)是更安全的上锁方式,因为直接声明方法的形式用的是类的锁,而声明代码块的形式用的是私有属性的锁,尤其是在服务器开发的时候,外面类的锁很容易被黑客获取,从而获取了攻击服务器的入口,而私有属性的私有性让黑客难以获取,所以它的锁就相对安全的多。

类同步

上面的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

时间: 2024-10-19 11:54:13

java concurrent之前戏synchronized的相关文章

Java Concurrent happens-before

happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. Th

java concurrent之ReentrantLock

在编码的过程中,有时候我们不得不借助锁同步来保证线程安全.synchronized关键字在上一篇博客中已经介绍:自从JDK5开始,添加了另一种锁机制:ReentrantLock. 二者的区别 1.lock是jdk5之后代码层面实现的,synchronized是JVM层面实现的. 2.synchronized在出现异常的时候能够自动释放锁,而lock必须在finally块中unlock()主动释放锁,否则会死锁. 3.在竞争不激烈的时候synchronized的性能是比lock好一点的,但是当竞争

Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

Java并发编程系列[未完]: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) 一.重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理.现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的.而操作系统实现线程之间的切换这就需要从用户态转换到核心

Java并发编程:Synchronized及其实现原理

Java并发编程系列[未完]: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题.从语法上讲,Synchronized总共有三种用法: (1)修饰普通方法 (2)修饰静态方法 (3)修饰代码块 接下

java中volatile、synchronized

先补充一下概念:Java 内存模型中的可见性.原子性和有序性. 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情.为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的.也就是一个线程修改的结果.另一个线程马上就能看到.比如:用volatile修饰的变量,就会具有可见性.volatile修饰的变量不允许线

Java Concurrent

Java Concurrent ExecutorService ExecutorService exec = Executors.newCachedThreadPool(); // create a cached pool ExecutorService exec = Executors.newFixedThreadPool(4); // fixed sized thread pool ExecutorService exec = Executors.newSingleThreadExecuto

java 同步锁(synchronized)

java 同步锁(synchronized) 在java中,Synchronized就是一把锁,他可以锁定一个方法,也可以锁定一个方法,我擦,其实这两个东西就是一样的.块不就是一个没有名字的方法么,方法就是一个有名字的块.本文就用块来测试.所谓锁,就是原子操作,把这个锁定的块作为一个整体,就像你上厕所,拉了就要擦屁屁,当然你也可以不擦,如果你不在意出现的问题的话.信号量Semaphore和这个Synchronized 其实实现的功能差不多,不过效率不同,使用的方式也不同.Synchronized

Java并发编程:synchronized

原文链接:      http://www.cnblogs.com/dolphin0520/p/3923737.html 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就来一起讨论下线程安全问题,以及Java中提供了什么机制来解决线程安全问题. 以下是本文的目录大纲: 一.什么时候会出现线程安全问题? 二.如何解决线程安全问题? 三.synchronized同步方法或者同步块 一.什么时候

【转】java并发编程:synchronized

原文地址:http://www.cnblogs.com/dolphin0520/p/3923737.html 原文作者:海子 转载请注明出处 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就来一起讨论下线程安全问题,以及Java中提供了什么机制来解决线程安全问题. 以下是本文的目录大纲: 一.什么时候会出现线程安全问题? 二.如何解决线程安全问题? 三.synchronized同步方法或者