🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **线程安全** 多个线程同时共享一个全局变量或静态变量时,在进行写入操作时可能会引发的数据冲突。 比如多窗口卖票,结果令人吃惊 ~~~ public class ThreadDemo implements Runnable { private int count= 10; @Override public void run() { while (count> 0) { if (count> 0) { ... Thread.sleep(100); ... System.out.println(Thread.currentThread().getName() + "卖了第" + (10 - count+ 1) + "张票"); count--; } } } public static void main(String[] args) { ThreadDemo t = new ThreadDemo(); Thread t1 = new Thread(t, "1号窗口"); Thread t2 = new Thread(t, "2号窗口"); t1.start(); t2.start(); } } ~~~ ## **内置锁(synchronized)** 解决线程安全问题可以使用synchronized内置锁 **方式一**:同步代码块 ``` public class ThreadDemo implements Runnable { private int count = 10; private static Object obj = new Object();// 锁的对象,可以是任意的对象 @Override public void run() { while (count > 0) { synchronized (obj) { if (count > 0) { ... Thread.sleep(1000); ... System.out.println(Thread.currentThread().getName() + "卖了第" + (10 - count + 1) + "张票"); count--; } } } } public static void main(String[] args) { ThreadDemo t = new ThreadDemo(); Thread t1 = new Thread(t, "1号窗口"); Thread t2 = new Thread(t, "2号窗口"); t1.start(); t2.start(); } } ``` **方式二**:同步函数 ``` public class ThreadDemo implements Runnable { private int count = 50; @Override public void run() { while (count > 0) { sale(); } } public synchronized void sale() { if (count > 0) { ... Thread.sleep(1000); ... System.out.println(Thread.currentThread().getName() + "卖了第" + (50 - count + 1) + "张票"); count--; } } public static void main(String[] args) { ThreadDemo t = new ThreadDemo(); Thread t1 = new Thread(t, "1号窗口"); Thread t2 = new Thread(t, "2号窗口"); t1.start(); t2.start(); } } ``` * 方式一锁的对象是obj,也可以是其它任意的对象 * 方式二锁的对象是当前类的字节码 * 如果将方式一中锁的对象换成this,那么他就和方式二中的锁是同一对象 ## **多线程的三大特性** * 原子性 一个操作的执行过程不会被其它因数打断,要么就不执行。原子性保证了线程数据一致性。 * 可见性 当多个线程访问同一个变量时,一个线程修改了变量的值,其他的线程能立即看到。 jmm模型中,共享变量存在于**主内存**中,**本地私有内存**存放的是共享变量的副本,线程执行过程是先写入本地内存, 再将本地内存中值刷新到主内存中,此时主内存会通知其它线程数据发生了改变,其它线程本地私有缓存就会失效。 * 有序性 程序执行的顺序按照代码的先后顺序执行,一般情况下,处理器由于要提高执行效率,对代码进行重排序,运行的顺序可能和代码先后顺序不同,但是结果一样。单线程下不会出现问题,多线程就会出现问题了。 ``` int a = 2 //语句1 int r =4 //语句2 a = a + 1 //语句3 r = a*a //语句4 则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4 但绝不可能 2-1-4-3,因为这打破了依赖关系。 ``` ## **volatile** * 保障多线程可见性 volatile修饰的变量可使线程本地缓存中的值及时刷新到主内存中去,从而保障可见性 ``` private volatile int count = 50; ``` volatile虽然能保证线程可见性,但是无法保证线程的原子性。 ``` 自增共享变量i++可拆分以下3步 1.从主内存取值; 2.执行+1; 3.值重新写回主内存 ``` 假设A线程执行完步骤2,在多线程的情况下可能让出时间片,其它线程将i的值赋成5写入到主内存中去,线程A再继续执行将新的值写如主内存,此时就会出现线程安全问题。 * 禁止指令重排序优化 volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)