为什么会有线程安全问题?
当多个线程同时共享一个全局变量或静态变量时,在做写的操作时,可能回发生线程冲突问题,也就是线程安全问题。(读的时候不会发生数据冲突问题);
解决方法:
- 使用多线程之间同步(内置锁[synchronized])或Lock锁;
- 使用锁的话,只能让一个线程执行执行,代码执行完成之后释放锁,然后才让其他线程进行执行,这样的话就可以解决线程不安全的问题;
- 同步:当多个线程共享一个资源时,不会受到其他线程的干扰;
package com.train;
class TrainThreads implements Runnable {
// 火车票票数
private int count = 10;
@Override
public void run() {
while (count > 0) { // 小于0 线程就会结束
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
sale();
}
}
/* 使用 synchronized 内置锁
* public synchronized void sale() {
if (count > 0) {
// 100 张火车票 - 当前火车票数 + 1 Thread.currentThread().getName() 获取线程名称
System.out.println(Thread.currentThread().getName() + " 出售 " + ((10 - count) + 1) + " 火车票 ");
count--;
}
}*/
public void sale() {
if (count > 0) {
// 100 张火车票 - 当前火车票数 + 1 Thread.currentThread().getName() 获取线程名称
System.out.println(Thread.currentThread().getName() + " 出售 " + ((10 - count) + 1) + " 火车票 ");
count--;
}
}
}
public class Test {
public static void main(String[] args) {
TrainThreads trainThread = new TrainThreads();
//创建线程1
Thread t1 = new Thread(trainThread,"窗口1");
//创建线程2
Thread t2 = new Thread(trainThread,"窗口2");
t1.start();
t2.start();
}
}
不加鎖执行结果:
package com.train;
class TrainThreads implements Runnable {
// 火车票票数
private int count = 10;
@Override
public void run() {
while (count > 0) { // 小于0 线程就会结束
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
sale();
}
}
//使用 synchronized 内置锁 (同步方法)
public synchronized void sale() {
if (count > 0) {
// 100 张火车票 - 当前火车票数 + 1 Thread.currentThread().getName() 获取线程名称
System.out.println(Thread.currentThread().getName() + " 出售 " + ((10 - count) + 1) + " 火车票 ");
count--;
}
}
/*public void sale() {
if (count > 0) {
// 100 张火车票 - 当前火车票数 + 1 Thread.currentThread().getName() 获取线程名称
System.out.println(Thread.currentThread().getName() + " 出售 " + ((10 - count) + 1) + " 火车票 ");
count--;
}
}*/
}
public class Test {
public static void main(String[] args) {
TrainThreads trainThread = new TrainThreads();
//创建线程1
Thread t1 = new Thread(trainThread,"窗口1");
//创建线程2
Thread t2 = new Thread(trainThread,"窗口2");
t1.start();
t2.start();
}
}
加锁执行结果:
注意:使用内置锁(sunchronized)。保证线程原子性,当线程进入方法的时候,自动获取锁,一旦锁被其他线程获取到后,其他线程就会等待。
特征:只有一个线程进行使用。程序执行完毕之后,会自动释放锁。
注意:加上内置锁,只能提高性能安全性,但是会降低程序的运行效率。 当锁被占用时,其他线程需要等待,这个时候就进入了休眠状态。当它获取锁的时候,会进行资源切换。资源的竞争。
内置锁也是重入锁,锁可以重复传递,重复使用。
内置锁也是互斥锁,A线程拿到锁,B线程必须等A线程释放锁之后才能获取资源。
使用方式:同步方法,同步代码块。
线程的三大特性:
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
什么是多线程死锁?
同步中嵌套同步,导致锁的资源无法释放。
ThreadLocal 和 Volatile 的用法?
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。(也就是说 ThreadLocal 为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量)。
Volatile 使用Volatile修饰的变量一旦被某个线程修改,修改的值会立刻被更新的主内存中,当其他线程需要读取时,可以立刻获取到修改后的值。
Java内存模型?
Java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。