三道java新手入门面试题,通往自由的道路–锁+Volatile

三道Java新手入门面试题攻略

一、什么是锁?

锁是一种同步机制,用于控制多个线程对共享资源的访问。当多个线程试图访问同一共享资源时,可能会导致数据不一致或者其他问题,而锁就可以保证同一时刻只有一个线程访问该共享资源,避免多线程并发访问发生问题。

Java提供了两种锁机制:synchronized关键字和Lock接口。

synchronized关键字

  1. synchronized方法

如果一个方法被synchronized关键字修饰,那么该方法称为同步方法。同一个对象的不同线程在调用该方法时,只能有一条线程能执行,其他线程需要等待该线程执行完毕后再进行调用。

示例代码:

public class SyncDemo implements Runnable{
    private int count = 0;

    public synchronized void increase() {
        count++;
    }

    public void run() {
        for (int i = 0;i < 10000;i++) {
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncDemo syncDemo = new SyncDemo();
        Thread thread1 = new Thread(syncDemo);
        Thread thread2 = new Thread(syncDemo);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + syncDemo.count);
    }
}
  1. synchronized块

除了同步方法外,synchronized关键字还可以放在代码块中,利用锁的机制保证多个线程对于共享资源的互斥访问。

示例代码:

public class SyncDemo implements Runnable{
    private static int count = 0;
    private Object lock = new Object();

    public void increase() {
        synchronized (lock) {
            count++;
        }
    }

    public void run() {
        for (int i = 0;i < 10000;i++) {
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncDemo syncDemo = new SyncDemo();
        Thread thread1 = new Thread(syncDemo);
        Thread thread2 = new Thread(syncDemo);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + count);
    }
}

Lock接口

Lock接口提供了比synchronized更细粒度的锁机制,它允许多个线程同时访问共享资源,但同一时刻只能有一个线程修改资源。它还提供了比synchronized更灵活的同步方式,例如可以选择公平锁或非公平锁,甚至是可以在条件变量上等待或唤醒线程。

ReentrantLock是Lock接口的一个具体实现,使用方式与synchronized类似。

示例代码:

public class LockDemo implements Runnable{
    private static int count = 0;
    private Lock lock = new ReentrantLock();

    public void increase() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public void run() {
        for (int i = 0;i < 10000;i++) {
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo lockDemo = new LockDemo();
        Thread thread1 = new Thread(lockDemo);
        Thread thread2 = new Thread(lockDemo);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + count);
    }
}

二、什么是Volatile?

Volatile是Java中的另一种同步机制,它保证对该变量的读写操作都是原子性的,即不允许出现多线程并发访问时的数据不一致问题。和synchronized关键字不同的是,Volatile是采用的可见性的思想,即对变量的更改会马上被其他线程所感知。

示例代码:

public class VolatileDemo implements Runnable {
    private volatile boolean flag = true;

    public void stop() {
        flag = false;
    }

    public void run() {
        while (flag) {
            System.out.println(Thread.currentThread().getName() + " is running...");
        }
        System.out.println(Thread.currentThread().getName() + " is stopped.");
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileDemo demo = new VolatileDemo();
        Thread thread = new Thread(demo);
        thread.start();
        Thread.sleep(1000);
        demo.stop();
    }
}

三、锁和Volatile的区别

锁和Volatile都可以用来保证线程安全,但是它们的实现机制不同:

  • 锁是采用了互斥访问的方式限制对共享资源的访问,而Volatile是通过保证可见性来避免数据不一致的问题。
  • 锁可以确保资源的唯一控制权,但是需要等待获取锁的线程释放锁之后其他线程才能继续执行,因此通常会对系统性能和响应时间产生影响;而Volatile则不存在这个问题,对系统性能的开销很小。
  • 锁适用于一些复杂的互斥访问场景,例如多线程交互时,为了避免死锁或者其他等待导致的问题;而Volatile适用于对性能要求更高的单个变量访问场景,例如flag变量。

示例代码:

共享变量a,要求a的值在所有线程之间同步,运用synchronzed和Volatile的线程池示例如下:

public class SyncAndVolatileDemo {

    private int a = 0;

    public synchronized void syncIncrease() {
        a++;
    }

    private volatile int b = 0;

    public void volatileIncrease() {
        b++;
    }

    public static void main(String[] args) throws Exception {

        SyncAndVolatileDemo demo = new SyncAndVolatileDemo();

        // 线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 同步方法访问示例
        executorService.submit(() -> {
            for (int i = 0; i < 10000; i++) {
                demo.syncIncrease();
            }
        });

        // Volatile变量访问示例
        executorService.submit(() -> {
            for (int i = 0; i < 10000; i++) {
                demo.volatileIncrease();
            }
        });

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println("a's value: " + demo.a);
        System.out.println("b's value: " + demo.b);
    }
} 

在同步访问a和Volatile访问b的线程池任务中,经过多次测试可以看出,同步访问的a最后的结果正常,而Volatile访问的b有时会发生丢失/重复值的情况。这表明了锁机制比Volatile更加可靠,在多线程环境中可以更好地保证数据的完整性和一致性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:三道java新手入门面试题,通往自由的道路–锁+Volatile - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Spring整合JPA与Hibernate流程详解

    关于Spring整合JPA与Hibernate的流程,我可以给你一个完整的攻略。首先,需要了解一些基础知识: JPA JPA(Java Persistence API)是一种规范,用于在Java应用程序中管理关系数据库的数据。 Hibernate Hibernate是一个开源的ORM(对象关系映射)框架,他实现了JPA规范。 Spring Spring是一个…

    Java 2023年5月19日
    00
  • Kafka多节点分布式集群搭建实现过程详解

    接下来我将详细讲解 “Kafka多节点分布式集群搭建实现过程详解” 的完整攻略。 1. 什么是Kafka Kafka 是一种高吞吐量的分布式发布订阅消息系统,应用于大规模的消息处理环境中,具有高可用、高性能的特点。 2. Kafka 集群搭建 2.1 环境准备 在搭建Kafka集群之前,需要准备好以下环境: Oracle JDK 1.8或以上版本 Zooke…

    Java 2023年5月20日
    00
  • Java读取数据库表

    Java读取数据库表 package com.easycrud.builder; import com.easycrud.utils.PropertiesUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; /** * @BelongsProjec…

    Java 2023年5月2日
    00
  • 常见的Java代码优化技巧有哪些?

    常见的Java代码优化技巧主要包括以下几个方面: 1.减少内存使用: Java程序运行时需要占用内存,因此减少内存使用可以提高Java程序的运行速度。具体方法包括: 避免使用过多的静态变量,因为静态变量会在程序启动时立即进行初始化,从而占用额外的内存空间。 避免在循环中创建多余的对象,因为对象创建也需要占用内存。 使用轻量级的容器,如ArrayList代替V…

    Java 2023年5月11日
    00
  • Java对象转Json,关于@JSONField对象字段重命名和顺序问题

    Java对象转Json是非常常见的操作,而在进行转换时,有时会遇到对象字段需要重命名或特定顺序的情况。这时候,就需要使用@JSONField注解来解决这些问题。 @JSONField注解 @JSONField注解是fastjson提供的注解,用于标识序列化和反序列化时的字段行为。该注解有以下常用属性: name:用于指定字段名称,将Java对象中的字段重命名…

    Java 2023年5月26日
    00
  • Java字符串的intern方法有何奥妙之处

    讲解Java字符串的intern方法 什么是intern方法 Java编程语言中,字符串是一种不可变对象,即一旦字符串对象创建之后,它的值就不能再发生变化。 在Java中,字符串的intern方法是一个产生常量池的方法。当一个Java程序中通过常量来创建字符串时,实际上可以理解为它会检查常量池中是否已经存在这个字符串对象,如果已经存在,则会返回常量池中该对象…

    Java 2023年5月27日
    00
  • centos7安装Tomcat7的教程图解

    CentOS7安装Tomcat7的教程图解 第一步:安装JDK 首先,要安装JDK,可以使用CentOS默认仓库中的OpenJDK或者Oracle官网下载。 示例1:使用CentOS默认仓库中的OpenJDK安装 sudo yum install java-1.8.0-openjdk-devel 示例2:从Oracle官网下载JDK安装 # 下载二进制文件 …

    Java 2023年5月19日
    00
  • java中的通用权限管理设计(推荐)

    Java中的通用权限管理设计(推荐) 简介 在Java应用程序开发过程中,通用权限管理设计可以有效地管理系统内不同用户的权限,做到安全可靠地管理用户访问数据的安全性和可靠性,避免了系统访问被恶意用户攻击,数据泄露和其他相关问题的出现。 设计 本文推荐一种常见的通用权限管理设计方案,使用RBAC(Role Based Access Control)模型,该模型…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部