同一对象
被多个线程操作
同时操作
以上被称为并发三要数--->并发
一旦存在并发就可能出现存在数据不准确、容器数据问题--->这些问题统称为线程不安全
同一进程的多个线程共享同一块存储空间--->会导致访问冲突
队列排队--->根据算法决定谁先用谁后用
一个资源操作完成在执行下一个资源
如何判断资源是否释放?
对象的锁--->每一个对象都有一个排它锁。表示该资源被占用了还是未被占用。
队列+锁既可以保证线程安全
多线程当中把队列和锁的操作称为线程的同步
多个线程访问同一个对象
某些线程还想修改这个对象
一种等待机制
多个需要同时对此对象的线程进入这个对象的等待池形成队列--->形成队列
前面的线程使用完毕后下一个线程再使用--->加上锁机制
目的:
为了保证数据在方法中被访问时的正确性
特点:
当一个线程获得对象的排它锁,独占资源
其他线程必须等待,使用后释放锁即可
一个线程持有锁会导致其他所有需要此锁的线程挂起
存在的问题:
在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时引起性能问题
一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
时间复杂度和空间复杂度不可能同时进行解决
private关键字保证数据对象只能被方法访问,所以synchronized是针对方法提出的一套机制
锁对象、锁资源
锁定一个具体的对象
锁定this(成员方法)--->本类中
锁定静态方法(锁定对象的模子,class方法(反射的时候会提及))--->本类中
在方法中会出现锁--->同步方法(用法一)
在块中会出现锁--->同步块(用法二)
用法:
//同步方法 public synchronized void method(int args){}
synchronized方法的特点:--->将一个大的方法声明为synchronized将会大大影响效率
*控制对"成员变量|类变量"对象的访问
每个对象对应一把锁
每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则所属线程阻塞
方法一旦执行久独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态--->就绪状态
举例:
假设一个方法操作对象的两个属性(A,B属性并非资源--->同一资源不同属性)
并发是修改资源会出问题。A属性读不改,B涉及修改。在方法上加锁会导致A,B属性都被锁住。对方想访问A属性而非B属性的时候也会导致线程阻塞,所以这个时候就需要用到synchronized块去锁住指定的属性
synchronized方法是最简单的。块太大会影响性能,块太小会锁不住资源
对象要锁对--->锁对方法操作的对象
性能问题--->锁资源的范围和锁资源的方法(块还是方法)(范围太大效率低下、范围太小导致线程数据不安全)