Java并发编程

  synchronized是Java中的关键字,在并发编程中被称为内置锁或者监视器锁。当用它来修饰一个方法或者一个代码块的时候能够保证同一时刻最多只有一个线程执行该段代码。

Java的内置锁相当于一种互斥锁,最多只有一个线程能够持有这种锁,故而由这个锁保护的同步代码块会以原子方式执行,多个线程在执行该代码时就不会相互干扰。

但由于被锁保护的同步块代码是以串行形式来访问的,即多个线程以独占的方式访问对象,而这也导致如果被锁保护的同步代码块的作用范围过大,会导致并发不良。

这里有必要简单讲一下内置锁的实现同步的原理:Java中的每一个对象都可以作为锁,synchronized用的锁其实存在Java对象头里(这也是为什么称其为内置锁的原因)。

而根据synchronized修饰的方法或方法块的不同,其锁的形式也不一。

  • 对于普通同步方法,锁是当前实例对象;
  • 对于静态同步方法,锁是当前类的Class对象;
  • 对于同步方法块,锁是synchronized括号内配置的对象;

以下是一个简单的例子,表示synchronized会根据其修饰对象不同而获取不同的锁。

public class Test {

    /**
     * 修饰方法,会对该类的实例进行加锁,该实例的所有synchronized方法都必须
     * 等待当前锁释放后才能访问
     */
    public synchronized void syn1(){
        //TODO

    }

    /**
     * 同步静态方法,会对类对象上锁,该类的其他实例的synchronized方法必须等当前锁释放后才能访问
     */
    public synchronized static void  syn3() {
        //TODO
    }
    /**
     * 同步指定对象,会对对象上锁,其他使用该对象的synchronized方法必须等当前锁释放后才能访问
     */
    public void  syn4() {        //obj仅代表要修饰的具体对象名
        synchronized (obj) {

        }
    }
    /**
     * 同步指定的类,会对类对象上锁,该类的其他实例的synchronized方法必须等当前锁释放后才能访问
     */
    public void syn5() {        //ClassName仅代表要修饰的具体类
        synchronized(ClassName.class) {
            //TODO
        }
    }

}

此外,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁(锁嵌套时),所以尽量避免无谓的同步控制。此外,为了并发的性能考虑,在实际使用synchronized关键字时,要注意确定synchronized保护的同步代码块大小合理。

为什么要重点提一下synchronized这个关键字呢?

  1. 在《Java并发编程实战》中提到,synchronized作为JVM的内置属性,它能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步,而基于类库的锁来实现这些功能则可能性不大。
  2. 内置锁的性能在Java6中已经有所提升,已经较为接近ReentrantLock。(而在Java5中ReentrantLock中是远远胜出的)
  3. 内置锁结构紧凑,而且危险性较低(ReentrantLock可能忘记调用unlock)

所以当内置锁可以满足需求时,尽可能使用内置锁,而仅当内置锁无法满足需求(如可中断、可定时、可轮询、公平性等)。简单的来讲,当需要对锁的获取和释放有更精确的控制要求时,考虑使用ReentrantLock。

时间: 2024-08-01 10:42:29

Java并发编程的相关文章

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java并发编程:Callable、Future和FutureTask(转)

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

Java并发编程 Volatile关键字解析

volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 根据volatile的语义,我们可以看到,volatile主要针对的是并发三要素(原子性,可见性和有序性)中的后两者有实际优化作用. 可见性: 线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作.

6、Java并发编程:volatile关键字解析

Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatil

7、Java并发编程:深入剖析ThreadLocal

Java并发编程:深入剖析ThreadLocal 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两个应用场景. 以下是本文目录大纲: 一.对ThreadLocal的理解 二.深入解析ThreadLocal类 三.ThreadLocal的应用场景 若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者

java并发编程实战学习(3)--基础构建模块

转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put方法将阻塞直到空间可用:如果队列为空,那么take方法将阻塞直到有元素可用.队列可以是有界的也可以是无界的. 如果生产者生成工作的速率比消费者处理工作的速率款,那么工作项会在队列中累计起来,最终好紧内存.同样,put方法的阻塞特性也极大地简化了生产者的编码.如果使用有界队列,当队列充满时,生产者将阻

Java 并发编程之任务取消(九)

Jvm关闭 jvm可正常关闭也可强行关闭,正常关闭有多种触发方式: 当最后一个正常(非守护,下面会讲到什么是守护线程)线程结束时 当调用system.exit时,或者通过其他特定于平台的方法关闭时(例如发送了SIGINT信号或键入Ctrl-c) 通过其他特定平台的方法关闭jvm,调用Runtime.halt或者在操作系统当中杀死JVM进程(例如发送sigkill)来强行关闭jvm. 关闭钩子 在正常关闭中,jvm首先调用所有已注册的关闭钩子,关闭钩子是指通过 Runtime.addShutdow

Java并发编程笔记 并发概览

并发概览 >>同步 如何同步多个线程对共享资源的访问是多线程编程中最基本的问题之一.当多个线程并发访问共享数据时会出现数据处于计算中间状态或者不一致的问题,从而影响到程序的正确运行.我们通常把这种情况叫做竞争条件(race condition),把并发访问共享数据的代码叫做关键区域(critical section).同步就是使得多个线程顺序进入关键区域从而避免竞争条件的发生. >>线程安全性 编写线程安全的代码的核心是要对状态访问操作进行管理,尤其是对共享的和可变的状态访问. 线

Java并发编程:锁的释放

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd