线程同步

:Thread的 suspend() 方法容易导致死锁,所以Java不再推荐使用该方法来暂停线程的执行。

同步代码块

synchronized加在代码块上。示例:

synchronized(obj){

}
  1. 上述代码中obj就是同步监视器
  2. 任何时刻都只能有一个线程可以获得同步监视器的锁定,当同步代码块执行完后,该线程就会释放对该同步监视器的锁定。
  3. 因为同步监视器的目的是:阻止两个线程对同一个共享资源进行并发访问。所以一般都用可能被并发访问的共享资源充当同步监视器。

同步方法

synchronized加在方法上。示例:

public synchronized void update(Object obj){

}
  1. 同步实例方法(非静态方法)的同步监视器,就是调用该方法的对象(即this)。要注意的是,是通过new创建的对象,而不是类。

关于synchronized类锁和对象锁的区别

Class A {

    // ==>对象锁:普通实例方法默认同步监视器就是this,即调用该方法的对象
    public synchronized methodA() {

    }

    public  methodB() {     
        // ==>对象锁:this表示是对象锁
        synchronized(this){  
        }
    }

    // ==>类锁
    public static synchronized methodC() {

    }

    public methodD(){
        // ==>类锁:A.class说明是类锁
        synchronized(A.class){}
    }

    // 普通方法:任何情况下调用时,都不会发生竞争
    public common(){

    }

}
  1. methodA,和methodB都是对当前对象加锁,即如果有两个线程同时访问同一个对象的methoA或methodB会发生竞争。如果两个线程访问的是不同对象的methodA和methodB则不会发生竞争。

  2. methodC和methodD是对类加锁,即如果两个线程同时访问同一个对象的methodC和methodD会发生竞争,且两个线程同时访问不同对象的methodC和methodD是也会发生竞争。

  3. 如果一个线程访问methodA或methodB,另一个线程访问methodC或methodD,则这两个线程不会发生竞争。因为一个是类锁另一个是对象锁。类锁和对象锁是两个不一样的锁,控制着不同的区域,它们互不干扰

5种类锁情况

同步块里加字符串的情况属于类锁,可能是因为String常量的唯一性导致的。具体我也不是很明白。

Class A {
    // 普通字符串属性
    private String val;
    // 静态属性
    private static Object staticObj;

    // ==>类锁情况1:synchronized修饰静态方法
    public static synchronized methodA() {

    }

    public methodB(){
        // ==>类锁情况2:同步块里的对象是类
        synchronized(A.class){}
    }

     public methodC(){
         // ==>类锁情况3:同步块里的对象是字符串
        synchronized("A"){}
    }

    public methodD(){
        // ==>类锁情况4:同步块里的对象是静态属性
        synchronized(staticObj){}
    }

    public methodE(){
        // ==>类锁情况5:同步块里的对象是字符串属性
        synchronized(val){}
    }

}

同步锁(Lock)

  1. Lock是Java5提供的一个强大的线程同步机制(通过显示定义同步锁对象来实现同步)。
  2. Lock可以显示的加锁、解锁。每次只能有一个线程对Lock对象加锁,线程访问资源之前应先获得Lock对象。
  3. Lock有ReadLock(读锁)、WriteLock(写锁)、ReentrantLock(可重入锁),常用的就是ReentrantLock
  4. 被相同锁保护的方法,可以相互调用。
  5. 示例:
public class Account
{
    // 定义可重入锁对象:一个线程可以对已被加锁的ReentrantLock锁再次加锁。
    private final ReentrantLock lock = new ReentrantLock();

    public void draw(Object obj)
    {
        // ===加锁===
        lock.lock();
        try
        {
            //需要保证线程安全的代码 
            doSomethiing();     
        }
        finally
        {
            // ===修改完成,释放锁===
            lock.unlock();
        }
    }


}

Lock与synchronized 的区别

推荐阅读:https://www.cnblogs.com/lemon-flm/p/7880119.html特别要注意和思考的是lock锁是类锁还是对象锁

  1. synchronized在发生异常时,会自动释放线程占有的锁;而Lock在发生异常时,如果没有主动通过unLock()释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  2. Lock可以让等待锁的线程响应中断去干别的事务,而synchronized却不行;使用synchronized时,等待的线程会一直阻塞直到拿到锁,不能够响应中断;

  3. 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  4. 在性能上,如果竞争资源不激烈,两者的性能差不多;而当竞争资源非常激烈时(即有大量线程同时竞争),Lock的性能要远远优于synchronized

  5. Lock可以提高多个线程读操作的效率
    当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作是不会发生冲突的。
    如果采用synchronized来实现同步,当多个线程都只是读操作时,若其中一个线程在进行读操作,其他线程就只能等待。而 Lock使用读锁时,可以使得多个线程之间只有读操作时不会发生竞争。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页