Java多线程——线程安全性

线程安全性

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的

public class A {

    public void test(){
        //....
    }
}

无状态对象是线程安全的,其不包含任何域,也不包含任何对其他类中域的引用,调用过程产生的临时状态也仅存在于线程栈上的局部变量中

竞态条件

public class A {

    private long count;

    public long getCount() {
        return count;
    }

    public void test() {
        //....
        count++;
    }
}

如果新增计数器记录方法调用次数,此时类变为非线程安全,对于“读取-修改-写入”复合操作过程,两个线程执行情况可能如下
Java多线程——线程安全性_第1张图片

由于不同的运行顺序会导致不一样的结果,称为竞态条件

原子性操作

对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作

public class A {

    private AtomicLong count;

    public long getCount() {
        return count.get();
    }

    public void test() {
        //....
        count.incrementAndGet();
    }
}

可使用原子变量类AtomicLong代替long确保所有对count的操作是原子性

内置锁

若两个原子变量相互影响,则又可能出现竞态条件,如下count为0时,线程1和2同时新增了count,但线程1再调用getCount()已为2导致错误

public class A {

    private AtomicLong count;
    private AtomicBoolean isOdd;

    public long getCount() {
        return count.get();
    }

    public void test() {
        //....
        count.incrementAndGet();
        isOdd.set(getCount() % 2 == 0);
    }
}

此时需要使用如下同步代码块,其包含两个部分

  • 作为锁的对象引用,每个java对象都可作为锁,称为内置锁
  • 这个锁保护的代码块
synchronized(lock){
	......
}

synchronized修饰方法时

  • 整个方法体为同步代码块
  • 锁就是方法调用所在的对象,若是static方法则以class对象作为锁

如上,可添加synchronized让两个变量的修改具有原子性

public class A {
	
	@GuardedBy("this")
    private long count;
	
	@GuardedBy("this")
    private boolean isOdd;

    public long getCount() {
        return count;
    }

    public synchronized void test() {
        //....
        count++;
        isOdd = getCount() % 2 == 0;
    }
}

内置锁是可重入的,即线程可以获得一个已经由它自己持有的锁(synchronized方法可互相调用),锁的颗粒度是线程而不是调用

活跃性与性能

上面用synchronized加锁方法可以保证原子性,但每次只有一个线程可以执行(即线程1调用了对象的synchronized方法,线程2想要调用其他synchronized方法时需等线程1释放锁),若方法耗时过长则会影响性能

public class A {

    @GuardedBy("this")
    private long count;
    @GuardedBy("this")
    private boolean isOdd;

    public long getCount() {
        return count;
    }

    public void test() {
        //....
        synchronized (this) {
            count++;
            isOdd = getCount() % 2 == 0;
        }
    }
}

如上,缩小锁的范围,只限制共享变量,避免其他操作同步

你可能感兴趣的:(#,java多线程,java,开发语言,android)