保证线程安全都有哪些方式 (四)

本文详细介绍了Java中实现线程的三种方式:Runnable、Thread及Callable,并探讨了synchronized关键字的使用及其三大特性:原子性、有序性和可见性。此外,还对比分析了ReentrantLock与synchronized的不同之处,以及Atomic类和Wait、Notify方法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现线程三种方式

java实现线程的方式总共有3中方式,一种是接口的形式,另一种是继承,还有一个中是接口的方式不过带有返回值,所以我们可以判断该线程是否成功

Runable


class judy implements Runnable{
    @Override
    public void run() {
        
    }
}

有的人可能会问,start和run的区别是什么,start方法让线程进行就绪状态,等待cpu分配资源, start里面调用了run方法, 而run方法仅仅是一个简单方法,并没有启用一个新的线程, 但是start不一样,因为当他得到cpu的时候,他就会启动一个新的线程

Thread

class judy extends Thread{
    @Override
    public void run() {
        super.run();
    }
    void test(){
        System.out.println("test");
    }
}

 judy test = new judy();
  test.start();

为什么有一个可以创建线程的方法,还会有一个,因为根据java的特性是单继承,所以我们还可以通过接口来创建线程

Callable

callable的主要作用就是可以获得该线程的返回值,然后根据返回值做处理

class judy implements Callable{
    @Override
    public Object call() throws Exception {
        return null;
    }
}

 public static void main(String[] args){
         Callable<String> callable = new judy();
         FutureTask<String> futureTask = new FutureTask<String>(callable);
         Thread thread = new Thread(futureTask);
        thread.start();
        futureTask.get();//获取线程返回值
         }
    }

Synchronized

线程同步,表示当多个线程同时使用的是一个对象的时候,sync只让一个运行,其他的必须等待

多个线程在同时运行的时候如果访问的不是不同对象那么他们就是线程安全的,否则就使用sync,来保证线程的同步运行

sync几种方式
1 class 锁
2 方法锁
3 代码块锁

class judye{

    public synchronized Object call(){
        return null;
    }
}

执行大概步骤

  1. 获取锁
  2. 执行方法
  3. 释放锁

可重入

可重入的意思指的是一个线程再获得锁之后,可以调用其他同样锁的代码 , 为什么他可重入, 是因为他在底层判断你是否是当前线程, 如果是当前线程那么可以继续执行, 如果不是当前的线程那么不允许执行

内存可见性

由于sync的同步作用所以我们是原子性操作, 但是sync同时也具有内存可见性, 在释放锁的时候, 所有写入都会写回到内存, 在获得锁之后, 都会从内存中读取最新的数据

三大特性

1原子性:一步操作,例如int i = 5, 而i++则不是原子性,因为她分为3步
2有序性:可以理解sync,同步
3 可见性:volatile,工作区与内存可见,同步

ReentrantLock

1 他实现了Lock接口, lock接口是jdk1.5的时候出来的,他实现结果跟sync是一个道理,不过他是finally来释放锁的

sync是jvm层面,lock是类层面,sync锁状态不可以判断,Lock的锁状态可以判断,sync不可中断, lock可以中断也有公平锁

public class ReentrantLock implements Lock

2ReentrantLock 总共实现两种方式,公平锁和非公平锁
我们先来看非公平锁

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
        //获取当前线程
            final Thread current = Thread.currentThread();
            //同步状态
            int c = getState();
            //线程可以进入,返回true
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断是否是当前线程
            else if (current == getExclusiveOwnerThread()) {
            //线程数加1
                int nextc = c + acquires;
                //抛出异常
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //不是当前线程返回false
            return false;
        }

释放锁

       protected final boolean tryRelease(int releases) {
       //减去释放
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //释放锁
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //保存减去值的状态
            setState(c);
            return free;
        }

具体内容可以看注释

公平锁

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁.,非公平锁肯能会导致解饿状态

Atomic

原子性

Wait和Notify

消息通知

在多线程环境中,为了避免数据竞争和保护共享资源的一致性,可以采用以下几种方式来实现线程安全: 1. 互斥锁(Mutex):使用互斥锁可以确保在同一时间只有一个线程访问共享资源。当一个线程获得了互斥锁后,其他线程需要等待该线程释放锁才能继续访问。这种方式可以防止多个线程同时修改数据,从而保证数据的一致性。 2. 读写锁(ReadWrite Lock):读写锁允许多个线程同时读取共享资源,但只允许单个线程进行写操作。这种方式适用于读操作频繁且修改操作较少的场景,可以提高并发性能。 3. 原子操作(Atomic Operation):原子操作是不可分割的操作,可以确保在并发环境中对共享资源的操作是原子性的。常见的原子操作包括原子整型、原子指针等,可以保证对共享资源的读写操作是线程安全的。 4. 条件变量(Condition Variable):条件变量用于线程之间的同步和通信。它允许一个或多个线程等待某个条件满足后再继续执行。条件变量通常与互斥锁一起使用,通过等待和唤醒机制来实现线程的同步。 5. 无锁算法(Lock-Free Algorithm):无锁算法是一种不使用互斥锁的并发编程技术。它通过使用原子操作和其他线程同步机制来实现线程安全,避免了锁带来的开销和竞争。无锁算法常用于高并发场景下的数据结构实现。 这些方式都可以用来实现线程安全,选择哪种方式要根据具体的应用场景和性能需求进行评估和选择。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王雪芬-ghqr-264962

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值