在并发编程中经常会碰到多个执行线程共享资源的问题。例如多个线程同时读写文件,共用数据库连接,全局的计数器等。如果不处理好多线程之间的同步问题很容易引起状态不一致或者其他的错误。
同步不仅可以阻止一个线程看到对象处于不一致的状态,它还可以保证进入同步方法或者块的每个线程,都看到由同一锁保护的之前所有的修改结果。处理同步的关键就是要正确的识别临界条件(critical section),即多线程访问/修改共享资源的代码块。
关键字synchronized可以保证同一时刻,只有一个线程可以执行某个方法或者某个代码块。 此外Java语言规范保证读写一个变量是原子的,除非long或者double.虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是并不保证一个线程写入的值对于另一个线程是可见的,线程间的通信,同步是必要的。
synchronized同步方法:
我们可以把synchronized关键字放在方法前,保证同时只有一个线程执行该方法。需要注意的是如果一个类的静态方法和实例方法都有synchronized,那么同意时刻允许一个线程访问静态方法,另一个线程访问实例方法。例如:
private static synchronized void stopRequested(){
stopRequest = true;
}
注意确保synchronized 方法仅仅是非常短的修改或者读取共享资源,否则会引起性能问题,因为synchronized本质上是互斥的。
synchronized同步块:
synchronized (this) {
// Java code
}
同步块可以让我们仅仅同步临界段,其余部分可以并行。 对于相互独立的共享资源,可以用不同的锁同步。例如同时售两个电影院的票:
public class Cinema {
private long vacanciesCinema1;
private long vacanciesCinema2;
private final Object controlCinema1, controlCinema2;
public Cinema() {
controlCinema1 = new Object();
controlCinema2 = new Object();
vacanciesCinema1 = 20;
vacanciesCinema2 = 20;
}
public boolean sellTickets1(int number) {
synchronized (controlCinema1) {
if (number < vacanciesCinema1) {
vacanciesCinema1 -= number;
return true;
} else {
return false;
}
}
}
public boolean sellTickets2(int number) {
synchronized (controlCinema2) {
if (number < vacanciesCinema2) {
vacanciesCinema2 -= number;
return true;
} else {
return false;
}
}
}
}
虽然读写primitive类型是原子的,但是如果线程之间不同步,还是会引起数据不一致问题,看下面这个例子:
public class TestStop {
private static boolean stopRequest;
public static void main(String[]args) throws InterruptedException{
Thread t = new Thread( new Runnable(){
public void run(){
int i = 0;
while(!stopRequest){
i++;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(2);
stopRequest = true;
}
}
这段代码有时候无法停止,因为子线程没看到主线程对stopRequest的改变。解决的办法是加上对共享变量的同步访问:
public class TestStop {
private static boolean stopRequest;
private static synchronized void stopRequested(){
stopRequest = true;
}
private static synchronized boolean getStopRequest(){
return stopRequest;
}
public static void main(String[]args) throws InterruptedException{
Thread t = new Thread( new Runnable(){
public void run(){
int i = 0;
while(!getStopRequest()){
i++;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(2);
stopRequested();
}
}
如果读写都没有同步,同步就没有效果。 针对此例,还可以用volatile,去掉同步。
在后续章节中,我们将介绍更多关于synchronize及Lock的内容。
分享到:
相关推荐
动力节点的Java课程适合绝对零基础的观看,教程中讲解了Java开发环境搭建、Java的基础语法、...每一个知识点都讲解的非常细腻,由浅入深。适合非计算机专业,想转行做Java开发的朋友,或者想让Java基础更扎实的小伙伴。
动力节点的Java课程适合绝对零基础的观看,教程中讲解了Java开发环境搭建、Java的基础语法、...每一个知识点都讲解的非常细腻,由浅入深。适合非计算机专业,想转行做Java开发的朋友,或者想让Java基础更扎实的小伙伴。
Java多线程初学者指南,共12篇文档,由浅入深,举例说明,包括一些疑点都解释得很清楚,类,方法,属性同步的区别等细节也都介绍得很全面,是个很好的学习资料。
动力节点的Java课程适合绝对零基础的观看,教程中讲解了Java开发环境搭建、Java的基础语法、...每一个知识点都讲解的非常细腻,由浅入深。适合非计算机专业,想转行做Java开发的朋友,或者想让Java基础更扎实的小伙伴。
多线程精讲下课程内容涵盖如下:l 线程的同步(线程安全问题)l 线程的死锁l 线程间通信l 线程组l 线程池l 多线程的第三种实现方案l 匿名内部类方式使用多线程l 定时器的使用l 多线程面试题
由浅入深,通过图解和手写代码,讲解Java版的多线程,主要...线程同步+各种锁的原理&手写实现 JDK多线程工具包中,若干种工具的原理和手写实现: ReentrantLock、CountDownLanuh、CyclicBarrier、Semaphore
该课程基于最新的java14平台,由浅入深的详细讲解了java集合框架的知识。帮助Java初学者掌握JSE相关知识。
12.3.1什么是线程同步 12.3.2一个有问题的案例 12.3.3如何解决 12.3.4小心线程死锁 12.4认识定时器 12.4.1为什么需要定时器 12.4.2如何使用定时器 12.5小结 第13章反射技术 13.1为什么要学习反射 13.1.1...
本书最后提供了一个附录,其中列出了Android与Java SE(Java Standard Edition,Java标准版)之间的不同之处。 . 在线资源 本书网站http://pragprog.com/titles/eband提供了以下资源。 本书使用的所有示例...
Asp.net 2.0功能体验,细节之Web控件(一) 隐藏控件 Asp.net 2.0功能体验,总体设计思想 Asp.net 2.0 WebPart使用经验点滴 革新:.NET 2.0的自定义配置文件体系初探 关于如何在ASP.NET 2.0中定制Expression ...