java--线程(二)(线程安全问题)
一、线程安全问题
问题描述:现在有A、B两个线程,同时往数组中存储数据,A线程存储“Hello”,B线程存储“World”
线程运行:假设A线程先抢到时间片,查找到下标为0 的位置没有数据,时间片到期,这时候下标为0的位置没有数据,然后B线程抢到时间片,查找到下标为0的位置没有数据,同时把“World”放到下标为0 的这个位置,之后A线程抢到时间片,继续执行之前的操作,把“Hello”放到下标为0的位置。这就造成了线程不安全。
线程不安全:
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
二、线程的同步
同步代码块:
synchronized(临界资源对象){//对临界资源对象加锁
//代码 (原子操作)
}
注:每一个对象都有一个互斥锁标记,用来分配给线程的。
只拥有对象互斥锁标记的线程,才能进入该对象加锁的同步代码块
线程退出同步代码块时,会释放相应的互斥锁标记
线程状态:
同步方法:
synchronized 返回值类型 方法名称(形参列表){//对当前对象(this)加锁
//代码 (原子操作)
}
注:只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
线程退出同步方法时,会释放相应的互斥锁标记
同步规则:
只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记
如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用
三、经典问题
死锁:
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁
一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
四、线程通信
等待:
public final void wait()
public final void wait(long timeout)
必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记。
同时此线程阻塞在o的等待队列中。释放锁,进入等待队列。
通知:
public final void notify()
public final void notifyAll()
必须在对obj加锁的同步代码块中。从obj的waiting中释放一个或全部线程。对自身没有任何影响