几种不同的加锁方式

1. 使用 t.s 加锁

javaCopy codeprivate void a(T t) {
    synchronized (t.s) { // 使用 t.s 作为锁对象
        System.out.println(t.s);
        // 模拟一些操作,表示持有锁的时间
        try {
            Thread.sleep(1000); // 暂停线程 1 秒,模拟长时间操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

粒度

  • 粒度:细粒度
  • 特性:使用 t.s 作为锁对象,允许同一类的不同实例之间的并发访问。

优缺点

  • 优点
    • 可以在多个线程中同时访问不同的 T 实例,只要它们的 s 字段不同,这样可以提高并发性。
  • 缺点
    • 如果多个 T 实例的 s 字段指向同一个字符串(例如,"Hello"),则会发生锁竞争,导致某些线程被阻塞。

2. 使用实例锁 obj 加锁

javaCopy codeObject obj = new Object();

private void a(T t) {
    synchronized (obj) { // 使用 obj 作为锁对象
        System.out.println(t.s);
        // 模拟一些操作,表示持有锁的时间
        try {
            Thread.sleep(1000); // 暂停线程 1 秒,模拟长时间操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

粒度

  • 粒度:中等粒度
  • 特性:所有调用此方法的线程共享同一个锁 obj

优缺点

  • 优点
    • 所有线程在调用 a(T t) 方法时必须等待获取同一个锁,确保在任意时刻只有一个线程在执行该方法。
    • 容易理解和使用,锁的管理比较简单。
  • 缺点
    • 可能导致性能瓶颈,特别是在高并发情况下,因为所有线程都必须排队等待同一个锁。
    • 对于不同的 T 实例,无法利用并发性。

3. 使用静态锁 static Object obj

javaCopy codestatic Object obj = new Object();

private void a(T t) {
    synchronized (obj) { // 使用 obj 作为锁对象
        System.out.println(t.s);
        // 模拟一些操作,表示持有锁的时间
        try {
            Thread.sleep(1000); // 暂停线程 1 秒,模拟长时间操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

粒度

  • 粒度:粗粒度
  • 特性:所有实例共享同一个静态锁 obj

优缺点

  • 优点
    • 适用于需要跨实例保护共享资源的情况,确保所有线程在任何 Example 类的实例中执行 a(T t) 方法时都互斥。
  • 缺点
    • 性能问题同样存在,所有线程需要等待同一个锁,导致潜在的性能瓶颈。
    • 在高并发场景中,可能导致不必要的线程阻塞。

4. 使用无锁 synchronized(块)

javaCopy codeprivate void a(T t) {
    synchronized { // 错误的用法
        System.out.println(t.s);
        // 模拟一些操作,表示持有锁的时间
        try {
            Thread.sleep(1000); // 暂停线程 1 秒,模拟长时间操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

粒度

  • 粒度:无粒度(错误的用法)
  • 特性:这段代码是错误的,因为 synchronized 必须要有一个锁对象,不能单独使用。

优缺点

  • 优点
    • 无法列出,因为这段代码无法编译和运行。
  • 缺点
    • 编译错误,无法使用。

5. 使用类锁 Example.class

javaCopy codeprivate void a(T t) {
    synchronized (Example.class) { // 使用类锁
        System.out.println(t.s);
        // 模拟一些操作,表示持有锁的时间
        try {
            Thread.sleep(1000); // 暂停线程 1 秒,模拟长时间操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

粒度

  • 粒度:粗粒度
  • 特性:所有 Example 类的实例在调用该方法时都会使用 Example.class 锁。

优缺点

  • 优点
    • 保证了所有线程对同一类的实例在调用 a(T t) 方法时互斥,确保了线程安全。
    • 类锁适用于需要控制对类级资源的访问。
  • 缺点
    • 性能瓶颈,所有线程在访问此类的实例时必须等待同一个锁,特别是在高并发情况下。
    • 可能导致不必要的线程阻塞。

总结

锁类型粒度优点缺点
t.s细粒度提高并发性,不同 T 实例之间可以并发访问。如果多个 T 实例的 s 字段相同,则可能竞争锁。
obj中等粒度确保在同一时刻只有一个线程执行,易于理解。性能瓶颈,所有线程竞争同一个锁。
static obj粗粒度确保跨实例的互斥,适合类级别的共享资源。性能瓶颈,所有线程等待同一个锁。
无锁 synchronized无粒度N/A编译错误,无法使用。
Example.class粗粒度保证所有线程在访问同一类的实例时互斥,适用于类级别的资源保护。性能瓶颈,导致不必要的线程阻塞。

在选择锁策略时,应根据具体应用场景和需求来决定,平衡性能和线程安全的需求。细粒度锁通常在并发访问较高的情况下能够提供更好的性能,而粗粒度锁则更易于管理和理解。

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x