1. 线程不安全
线程不安全代码:
代码语言:javascript复制public class ThreadDemo13 {
static class Counter{
public static int count = 0;
public void increase(){
count ;
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i ){
counter.increase();
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i ){
counter.increase();
}
}
};
t1.start();
t2.start();
t1.join();//当执行这里的时候,线程就阻塞了,一直到t1结束,才会往下执行
t2.join();
System.out.println(Counter.count);
}
}
以上代码有两个新的线程,此时最终的运行结果不唯一
2. 为什么会出现线程不安全的情况呢?
1.线程是抢占式执行的(根本原因)
2.自增操作不是原子的,每次 ,都能分为以下三个步骤 a)把内存中的数据读取到CPU (load) b)把CPU中的数据 1 (incr) c)把计算结束的数据写回到内存 (save) 当CPU执行到任意一步骤的时候,调度器随时都有可能调度走,来让其他线程来执行
3.多个线程尝试修改同一个变量
4.内存可见性导致的线程安全问题
5.指令重排序(在编译器编译代码时,会对指针进行优化,调整指令的先后顺序,保证原有逻辑不变的情况下提高程序的运行效率)
3. 加锁 synchronized
1)实现了原子性 性能比较低
加上 synchronized 之后不一定立即就能成功,如果发现当前的锁已经被占用,该代码就会阻塞等待,一直等到之前的线程释放,才可能会获取到这个锁
以下代码假设线程一先获取到锁,那么线程二再尝试获取锁就会阻塞等待,线程一的运行不会受到影响。当线程一释放锁之后,线程二才有可能获取到锁。如果此时线程一中出现死锁,一旦锁死,就解不开了
代码语言:javascript复制public class ThreadDemo13 {
static class Counter{
public static int count = 0;
//表示进入此方法之前会尝试加锁
//increase方法执行完毕后会自动解锁
//这里就相当于是针对counter这个对象进行加锁
//进入方法内部,把加锁状态设为true,执行完成这个方法之后,就把加锁状态设为false
synchronized public void increase(){
count ;
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i ){
counter.increase();
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i ){
counter.increase();
}
}
};
t1.start();
t2.start();
t1.join();//当执行这里的时候,线程就阻塞了,一直到t1结束,才会往下执行
t2.join();
System.out.println(Counter.count);
}
}
此时运行结果就唯一了
2)以下是加锁和非加锁的对照: StringBuffer(加锁了) / StringBuilder(没加锁) Vector(加锁了) / ArrayList(没加锁) HashTable (加锁了)/ HashMap(没加锁)
3)理解synchronized具体使用 synchronized是可以灵活加锁的
- 加到普通方法前:表示锁this 如果synchronized关键字写到方法前面,那就相当于是给当前对象来加锁
- 加到静态方法前:表示锁当前类的类对象——反射
- 加到某一代码块之前:显示指定给某个对象加锁
public class ThreadDemo14 {
static class Test{
public void method(){
//这里括号为this,就相当于是给当前创建的对象加锁(t)
synchronized (this){
System.out.println("hh");
}
}
}
public static void main(String[] args) {
Test t = new Test();
t.method();
}
}