如果一个类的对象在多线程程序中没有导致竞争状态,则称这样的类为线程安全的( thread-safe) 。
多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题多线程的问题,又叫 Concurrency
问题
1. 线程同步概念
当两个任务以一种会引起冲突的方式访问一个公共资源时,会引起竞争
。
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AccountWithoutSync {
private static Account account = new Account();
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for(int i = 0; i < 100; i++) //创建100 个在线程池executor 中执行的线程
executor.execute(new AddPennyTask());
executor.shutdown();
while(!executor.isTerminated()) { //测试线程是否终止
}
System.out.println("what is balance?" + account.getBalance());
}
private static class AddPennyTask implements Runnable{
public void run() {
account.deposit(1);
}
}
private static class Account{
private int balance = 0;
public int getBalance() {
return balance;
}
public void deposit(int amount) {
int newBalance = balance + amount;
try {
Thread.sleep(5);
}
catch(InterruptedException ex) {
}
balance = newBalance;
}
}
}
2. synchronized
为避免竞争状态,应该防止多个线程同时进入程序的某一特定部分,程序中的这部分称为临界区( critical regioD) 。
可以使用关键字synchronized 来同步方法,以便一次只有一个线程可以访问这个方法。
对于竞争冲突问题,有几种办法可以解决。
-
一种办法是通过在deposit 方法中添加关键宇synchronized ,使Account 类成为线程安全的.public synchronized void deposit(int amount)
-
一个同步方法在执行之前需要 加锁( 15.3内容 )。
Object someObject =new Object();
synchronized (someObject){
//此处的代码只有占有了someObject后才可以执行
}
synchronized 表示当前线程独占对象 someObject
当前线程独占了对象someObject,如果有其他线程试图占有对象someObject,就会等待,直到当前线程释放对someObject的占用。
someObject 又叫同步对象,所有的对象,都可以作为同步对象。为了达到同步的效果,必须使用同一个同步对象
释放同步对象的方式: synchronized 块自然结束,或者有异常抛出
3. 同步语句
当执行方法中某一个代码块时,同步语句不仅可用于对 this 对象加锁,而且可用于对任何对象加锁。这个代码块称为同步块。
synchronized (expr) {
statements;
}
表达式expr 求值结果必须是一个对象的引用。如果对象已经被另一个线程锁定,则在
解锁之前,该线程将被阻塞。当获准对一个对象加锁时,该线程执行同步块中的语句,然后解除给对象所加的锁。
4. 线程安全类
如果一个类,其方法都是有synchronized修饰的,那么该类就叫做线程安全的类
同一时间,只有一个线程能够进入 这种类的一个实例 的去修改数据,进而保证了这个实例中的数据的安全(不会同时被多线程修改而变成脏数据)
比如StringBuffer和StringBuilder的区别
StringBuffer的方法都是有synchronized修饰的,StringBuffer就叫做线程安全的类
而StringBuilder就不是线程安全的类