线程安全
线程安全问题通常发生于多线程操作中。
线程不安全的原因
线程执行方式:线程间是抢占式执行的,谁先抢到谁就执行,用户是不能干预的
操作不是原子性的:线程的自增操作主要是读取数据,加1,放回内存,在线程1修改的过程中其他线程都是可以读取数据进行操作的。
![](https://i-blog.csdnimg.cn/blog_migrate/a9fa00afec84fd57deb32e5d4c2a4464.png)
当多个线程操作同一个变量时,由于读取修改时间不同,读到的也许是其他线程未修改完的数据。
![](https://i-blog.csdnimg.cn/blog_migrate/09fbbc82a83e9a0746925e81fea5474b.png)
比如当线程1做自增操作,修改后还没放回内存时,线程2也来读取数据,也做了自增操作,但结果是线程1和2一共做了两次自增操作,而num只增了一次,试想一下,如果在我们所写的程序中,我们本希望两个线程可以互不干扰的操作一个变量,但最后却因为线程的同时操作导致结果不是预期的那样,就像下面的程序:
写三个线程,让这三个线程都对num做1000次自增,预期结果:num=3000
ThreadA
public class ThreadA extends Thread{
public void run() {
for(int i=0;i<1000;i++) {
Manage.num++;
}
}
}
ThreadB
public class ThreadB extends Thread{
public void run() {
for(int i=0;i<1000;i++) {
Manage.num++;
}
}
}
ThreadC
public class ThreadC extends Thread{
public void run() {
for(int i=0;i<1000;i++) {
Manage.num++;
}
}
}
启动线程
public class Manage {
public static int num=0;
static Object object=new Object();
public static void main(String[] args) {
ThreadA threadA=new ThreadA();
ThreadB threadB=new ThreadB();
ThreadC threadC=new ThreadC();
threadC.start();
threadB.start();
threadA.start();
try {
threadA.join();//等待线程执行完成
threadB.join();
threadC.join();
}catch(InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(num);
}
}
运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/511da67698e2ea617e59303f5928339e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4a547fe6a998a7495df643b7b9ac5d03.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9566a2af77c7485734393b01770fbae7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/657200f3420c5178da92593d0badc62e.png)
可以看到四次运行结果,只有最后一次达到预期,那么如何解决线程不安全的问题呢?
synchronized 关键字
想要保证线程自增过程中,其他线程不能修改,就要给修改变量的线程上锁,因为锁具有互斥性,同线程只有一个获取到锁,其他线程如果要获取就会发生阻塞,等线程结束释放锁,其他线程才可以竞争锁。
关键字的使用方法:
加到普通方法之前,将整个方法上锁
加到静态方法前,将类的类对象上锁
加到代码块前,将某个对象上锁
下面给三个线程num上锁:
public void run() {
for(int i=0;i<1000;i++) {
synchronized (Manage.object) {
Manage.num++;
}
}
}
运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/852dbaf1165a51b590f7460e4d52dfa0.png)
运行多次结果都是3000