并发:多个线程对一个资源进行竞争
并行:指应用能够同时执行不同的任务
当我们想使用多线程操作一个资源时,且多个线程对资源有读写操作时,很容易出现线程安全问题。
线程同步机制
同步在安全的同时,却降低了效率。
java中有三种方式完成同步操作
- 同步代码块。
- 同步方法。
- 锁机制
同步代码块
- 同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
synchronized(同步锁){
需要操作的同步代码
}
1、代码块中的锁对象可以时任意的对象
2、但是必须保证多个线程使用的时同一个
3、作用:吧同步代码块锁住,同一时间只能有一个线程访问
Object lock = new Object();
@Override
public void run() {
while(true){
synchronized (lock) {
}
在同步代码块中的资源,当某个线程获得了执行权后,其余的线程处于阻塞状态,等待执行中的线程释放锁。
同步方法
- 同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外 等着。
把需要同步的代码放在一个方法中,在方法上加上synchronized。
public synchronized void method(){
可能会产生线程安全问题
}
- 对于非static方法,同步锁就是this
- 对于static方法,我们使用当前方法所在类的字节码对象(类.class)
举例:
class Person{
private int money;
public synchronized int getMoney() {
return money;
}
public synchronized void setMoney(int money) {
this.money = money;
}
public synchronized void add() {
this.money++;
}
}
class ThreadNew extends Thread{
private Person permy;
public ThreadNew(Person per) {
permy=per;
}
@Override
public void run() {
for(int i=0;i<10000;i++) {
permy.setMoney(permy.getMoney()+1);
// permy.add();
}
}
}
ThreadNew 下如果用permy.setMoney(permy.getMoney()+1);这种方法,在set和get上分别加上锁,当代码执行的时候,因为读写操作的锁分开,a线程读操作结束会释放他的锁,b线程就可以进行读取操作,a数据还没来得即写回数据,b就可能读取到错误的数据。
permy.add();如果调用这个方法,因为读写在一起,保证了数据的读写操作。
防止数据不正常,把读写操作一定要写在一个方法里。锁起来,保证每个线程都能读写到正确数据
锁机制
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
- public void lock() :加同步锁。
- public void unlock() :释放同步锁。
使用步骤:
1、在成员位置创建一个ReentrantLock对象
2、在可能出现安全问题的代码前调用Lock接口的中的方法lock获取锁
3、在可能出现安全问题的代码后调用Lock接口的中的方法unlock获取锁
Lock lock = new ReentrantLock();
public void run(){
while(true){
lock.lock();
.....
.....
.....
lock.unlock();
}
}