认识多线程(二)、线程安全

认识多线程(二)、线程安全

在Java中,线程安全是指多个线程访问共享资源时,不会出现数据错乱、状态异常等问题,保证程序能够按照设计的预期进行正确的操作。在多线程环境下,线程安全是非常重要的,因为多个线程并发访问共享资源可能导致竞态条件(Race Condition)、死锁(Deadlock)、数据不一致等问题。

实现线程安全的方式通常包括以下几种:

  1. 互斥同步:通过互斥机制来确保在同一时刻只有一个线程访问共享资源。在Java中,可以使用synchronized关键字或者ReentrantLock等锁机制来实现互斥同步。
  2. 原子操作:通过使用原子类(Atomic Class)来实现对共享资源的原子操作,避免了使用锁机制而带来的性能开销。
  3. 不可变对象:设计不可变对象可以避免并发环境下的数据竞争,因为不可变对象的状态不会改变。
  4. 线程本地存储(Thread-local storage):将共享的资源复制多份,每个线程都有自己的副本,从而避免了共享资源的竞争。
  5. 同步容器:使用ConcurrentHashMapCopyOnWriteArrayList等并发容器来代替同步容器,避免手动加锁。

解决线程安全的方式:🔒

一、认识线程锁

线程锁(也称为互斥锁)是多线程编程中用于保护共享资源的一种同步机制。它可以确保在同一时刻只有一个线程可以访问被保护的资源,从而避免多个线程同时修改共享数据而导致数据不一致或其他意外情况的发生。

二、对象和this锁

类锁和对象锁(this锁)在Java中是实现多线程同步的两种不同方式,它们有以下区别:

  1. 作用范围:

    • 类锁:类锁是针对整个类的,当一个线程获取了该类的类锁后,其他线程无法获取该类的类锁,无论是哪个实例调用该类的同步方法都会受到限制。
    • 对象锁(this锁):对象锁是针对对象实例的,每个对象实例都有自己的对象锁。当一个线程获取了某个对象实例的对象锁后,其他线程无法获取该对象实例的对象锁,但其他对象实例的对象锁不受影响。
  2. 获取方式:

    • 类锁:可以通过synchronized修饰静态方法或者使用类的Class对象来获取类锁。
    • 对象锁(this锁):通过synchronized修饰非静态方法或者代码块,并以当前对象实例(this)作为锁对象来获取对象锁。
  3. 影响范围:

    • 类锁:影响整个类的所有实例,因为类锁是针对整个类的。
    • 对象锁(this锁):仅影响持有该对象锁的对象实例,不同对象实例之间的对象锁互不影响。

三、对象锁(this锁)

在Java中,this锁是一种对象级别的锁,也称为对象锁。当一个线程调用一个对象的同步方法时,它就获得了该对象实例的锁,其他线程必须等待该线程释放锁才能继续执行。这种锁定机制可以确保在同一时间内只有一个线程可以执行该对象的同步方法或者同步块。

使用this锁的常见方式有两种:

  1. 同步方法:通过在方法声明中使用synchronized关键字,可以将整个方法变成同步方法,这意味着该方法在被调用时会自动获取对应对象的锁。
java 复制代码
javaCopy Code
public synchronized void someMethod() {
    // 同步代码块
}
  1. 同步块:通过在代码块中使用synchronized关键字并指定锁对象为this,可以实现同步块。
java 复制代码
javaCopy Code
public void someMethod() {
    synchronized (this) {
        // 同步代码块
    }
}

需要注意的是,this锁是针对对象实例的,因此每个对象实例都有自己的this锁。不同对象实例之间的this锁互不影响,因为它们代表不同的对象。另外,this锁只在同一个对象实例内部起作用,不会对其他实例产生影响。

四、类锁

在 Java 中,类锁是一种用于控制对静态成员变量或静态方法的并发访问的锁机制。与对象锁不同,类锁是基于类的 Class 对象的,因此它作用于整个类而不是特定的对象实例。

类锁可以通过以下两种方式来获取:

  1. 使用 synchronized 关键字修饰静态方法:
Java 复制代码
javaCopy Code
public class MyClass {
    public static synchronized void staticMethod() {
        // 静态同步方法中的同步代码块
    }
}
  1. 使用类的 Class 对象作为锁对象进行同步控制:
java 复制代码
javaCopy Code
public class MyClass {
    public void method() {
        synchronized (MyClass.class) {
            // 同步代码块
        }
    }
}

类锁的特点包括:

  • 类锁是针对整个类的,因此它作用于所有该类的实例以及静态成员。
  • 类锁可以控制对静态成员变量和静态方法的并发访问。
  • 与对象锁不同,类锁是全局唯一的,因此对该类的所有实例都起作用。
  • 获取类锁的方式可以是使用 synchronized 关键字修饰静态方法,也可以是使用类的 Class 对象作为锁对象来进行同步控制。

五、this锁和类锁简单应用

实现一个简单的抢票demo

1、使用this锁实现

java 复制代码
package com.example.mayikttest.Thread.Synchronize;

public class ThreadCount implements Runnable {
    private static Integer count = 100; // 一百张票
    private String lock = "lock";

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }
    private void cal() {  // 修饰非静态方法
        synchronized (this) { // 修饰代码块
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }

    }

    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

2、实现类锁实现

java 复制代码
package com.example.mayikttest.Thread.Synchronize;


public class ThreadCount3 implements Runnable {
    private static Integer count = 100;
    private static Object lock = ThreadCount.class; // 类对象作为锁

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }

    private void cal() {
        synchronized (lock) { // 使用类对象作为锁
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                // 异常处理
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

3、线程池使用

java 复制代码
package com.example.mayikttest.Thread.Synchronize;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadCount3 implements Runnable {
    private static Integer count = 100;
    private static Object lock = ThreadCount.class; // 类对象作为锁

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }
    private void cal() {
        synchronized (lock) { // 使用类对象作为锁
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                // 异常处理
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        // 使用线程池Executors
        ExecutorService executor = Executors.newFixedThreadPool(8); // 创建固定大小的线
        executor.execute(threadCount);
    }
}
相关推荐
摇滚侠3 小时前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯5 小时前
Spring Boot测试框架详解
java·spring boot·后端
你的人类朋友6 小时前
什么是断言?
前端·后端·安全
程序员小凯7 小时前
Spring Boot缓存机制详解
spring boot·后端·缓存
i学长的猫7 小时前
Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
后端·ruby on rails·ruby
用户21411832636028 小时前
别再为 Claude 付费!Codex + 免费模型 + cc-switch,多场景 AI 编程全搞定
后端
茯苓gao8 小时前
Django网站开发记录(一)配置Mniconda,Python虚拟环境,配置Django
后端·python·django
Cherry Zack8 小时前
Django视图进阶:快捷函数、装饰器与请求响应
后端·python·django
爱读源码的大都督9 小时前
为什么有了HTTP,还需要gPRC?
java·后端·架构
码事漫谈9 小时前
致软件新手的第一个项目指南:阶段、文档与破局之道
后端