【并发多线程】java并发中的Synchronized关键词

简介: 【并发多线程】java并发中的Synchronized关键词

如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。

java提供了synchronized关键字,方便我们实现上述操作。


为什么要同步

我们举个例子,我们创建一个类,提供了一个setSum的方法:


public class SynchronizedMethods {
    private int sum = 0;
    public void calculate() {
        setSum(getSum() + 1);
    }
}

如果我们在多线程的环境中调用这个calculate方法:

@Test
public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods summation = new SynchronizedMethods();
    IntStream.range(0, 1000)
            .forEach(count -> service.submit(summation::calculate));
    service.shutdown();
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, summation.getSum());
}

按照上面的方法,我们预计要返回1000, 但是实际上基本不可能得到1000这个值,因为在多线程环境中,对同一个资源进行同时操作带来的不利影响。

那我们怎么才能够建线程安全的环境呢?


Synchronized关键词

java提供了多种线程安全的方法,本文主要讲解Synchronized关键词,Synchronized关键词可以有很多种形式:


Instance methods

Static methods

Code blocks

当我们使用synchronized时,java会在相应的对象上加锁,从而在同一个对象等待锁的方法都必须顺序执行,从而保证了线程的安全。


Synchronized Instance Methods

Synchronized关键词可以放在实例方法的前面:


   public synchronized void synchronisedCalculate() {
        setSum(getSum() + 1);
    }

看下调用结果:

@Test
public void givenMultiThread_whenMethodSync() {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods method = new SynchronizedMethods();
    IntStream.range(0, 1000)
      .forEach(count -> service.submit(method::synchronisedCalculate));
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, method.getSum());
}

这里synchronized将会锁住该方法的实例对象,多个线程中只有获得该实例对象锁的线程才能够执行。

Synchronized Static Methods

Synchronized关键词也可以用在static方法前面:

public static synchronized void syncStaticCalculate() {
    staticSum = staticSum + 1;
}

Synchronized放在static方法前面和实例方法前面锁住的对象不同。放在static方法前面锁住的对象是这个Class本身,因为一个Class在JVM中只会存在一个,所以不管有多少该Class的实例,在同一时刻只会有一个线程可以执行该放方法。

@Test
public void givenMultiThread_whenStaticSyncMethod() throws InterruptedException {
    ExecutorService service = Executors.newCachedThreadPool();
    IntStream.range(0, 1000)
            .forEach(count ->
                    service.submit(SynchronizedMethods::syncStaticCalculate));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);
    assertEquals(1000, SynchronizedMethods.staticSum);
}

Synchronized Blocks

有时候,我们可能不需要Synchronize整个方法,而是同步其中的一部分,这时候,我们可以使用Synchronized Blocks:

public void performSynchronizedTask() {
    synchronized (this) {
        setSum(getSum() + 1);
    }
}

我们看下怎么测试:

@Test
public void givenMultiThread_whenBlockSync() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods synchronizedBlocks = new SynchronizedMethods();
    IntStream.range(0, 1000)
            .forEach(count ->
                    service.submit(synchronizedBlocks::performSynchronizedTask));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);
    assertEquals(1000, synchronizedBlocks.getSum());
}

上面我们同步的是实例,如果在静态方法中,我们也可以同步class:

public static void performStaticSyncTask(){
    synchronized (SynchronizedMethods.class) {
        staticSum = staticSum + 1;
    }
}

我们看下怎么测试:

@Test
public void givenMultiThread_whenStaticSyncBlock() throws InterruptedException {
    ExecutorService service = Executors.newCachedThreadPool();
    IntStream.range(0, 1000)
            .forEach(count ->
                    service.submit(SynchronizedMethods::performStaticSyncTask));
    service.shutdown();
    service.awaitTermination(100, TimeUnit.MILLISECONDS);
    assertEquals(1000, SynchronizedMethods.staticSum);
}
相关文章
|
1天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
13 0
|
1天前
|
Java
Java中的并发编程:理解和应用线程池
【4月更文挑战第23天】在现代的Java应用程序中,性能和资源的有效利用已经成为了一个重要的考量因素。并发编程是提高应用程序性能的关键手段之一,而线程池则是实现高效并发的重要工具。本文将深入探讨Java中的线程池,包括其基本原理、优势、以及如何在实际开发中有效地使用线程池。我们将通过实例和代码片段,帮助读者理解线程池的概念,并学习如何在Java应用中合理地使用线程池。
|
5天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
5天前
|
安全 Java 开发者
Java并发编程:深入理解Synchronized关键字
【4月更文挑战第19天】 在Java多线程编程中,为了确保数据的一致性和线程安全,我们经常需要使用到同步机制。其中,`synchronized`关键字是最为常见的一种方式,它能够保证在同一时刻只有一个线程可以访问某个对象的特定代码段。本文将深入探讨`synchronized`关键字的原理、用法以及性能影响,并通过具体示例来展示如何在Java程序中有效地应用这一技术。
|
6天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
6天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
6天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
7天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
7天前
|
Java
浅谈Java的synchronized 锁以及synchronized 的锁升级
浅谈Java的synchronized 锁以及synchronized 的锁升级
8 0
|
7天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
15 1