一.什么是线程安全
当多个线程同时共享,同一个全局变量或静态变量,做写操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作时不会发生数据冲突问题。
public class ThreadDemo {
public static void main(String[] args) {
One one = new One();
Thread t1 = new Thread(one);
Thread t2 = new Thread(one);
t1.start();
t2.start();
}
}
class One implements Runnable {
private int ticketCount = 10;
public void run() {
while(ticketCount > 0) {
System.out.println(Thread.currentThread().getName() + "出售了车票:" + ticketCount);
ticketCount--;
}
}
}
//控制台打印
Thread-1出售了车票:10
Thread-0出售了车票:10
Thread-1出售了车票:9
Thread-0出售了车票:8
Thread-1出售了车票:7
Thread-0出售了车票:6
Thread-1出售了车票:5
Thread-0出售了车票:4
Thread-1出售了车票:3
Thread-0出售了车票:2
Thread-1出售了车票:1
结论发现:多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。
二.线程安全解决方案
1.同步代码块
synchronized(对象) {
//可能会发生线程冲突问题
}
对象如同锁,持有锁的线程可以在同步中执行,没持有锁的线程即使获取cpu的执行权,也进不去。
同步的前提
必须要有两个或者两个以上的线程
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源,抢锁的资源
2.同步函数
在方法上修饰synchronized称为同步函数,同步函数使用this锁
public synchronized void sale() {
//要执行的代码
}
3.静态同步函数
方法上加入static关键字,使用synchronized关键字修饰,静态同步代码块使用的锁是,该函数所属字节码文件对象
public synchronized static void sale() {
//要执行的代码
}
三.多线程死锁
同步中嵌套同步,导致无法释放锁
public class ThreadDemo {
public static Object object1 = new Object();
public static Object object2 = new Object();
public static void main(String[] args) {
One one = new One();
Thread t1 = new Thread(one);
t1.start();
Two two = new Two();
Thread t2 = new Thread(two);
t2.start();
}
}
class One implements Runnable {
public void run() {
while(true) {
synchronized (ThreadDemo.object1) {
System.out.println("oneObject1");
synchronized (ThreadDemo.object2) {
System.out.println("oneObject2");
}
}
}
}
}
class Two implements Runnable {
public void run() {
while(true) {
synchronized (ThreadDemo.object2) {
System.out.println("twoObject2");
synchronized (ThreadDemo.object1) {
System.out.println("twoObject1");
}
}
}
}
}
//控制台打印
oneObject2
oneObject1
oneObject2
oneObject1
oneObject2
oneObject1
oneObject2
oneObject1
twoObject2
线程one拿了object1线程two拿了object2,都在等待对方释放锁。双方都不释放锁,产生死锁。
四.多线程三大特性
1.原子性:即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
2.可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值
3.有序性:程序执行的顺序按照代码的先后顺序执行
五.java内存模型
java内存模型简称jmm,jmm决定一个线程对共享变量的写入时,能对一个线程可见。线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写的副本。
每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。
六.volatile关键字
volatile关键字的作用是变量在多个线程之间可见。强制每次读取都会去主内存中取值。volatile不具备原子性。
volatile与synchronized的区别
1.volatile轻量级,只能修饰变量。synchronized重量级还弄修饰方法。
2.volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
synchronized不仅保证可见性,而且还保证原子性,因为,只有获取了锁的线程才能进入临界区,从而保存临界区中的所有语句都全部执行,多个线程争抢synchronized锁对象时,会出现阻塞。
线程安全性包括1.可见性 2.原子性
volatile并不能保证线程安全性,而synchronized则可实现线程的安全性。
七.atomiclnteger原子类
Atomiclnteger是一个提供原子操作的integer类,通过线程安全的方式操作加减。