多个线程访问一些程序中的资源时,会发生资源抢占。我们用线程同步来解决这个问题,保证线程安全。
如果有一个全局变量 int sum = 1;有A,B两个线程对sum进行操作,他们获取资源时都是sum = 1,而线程A对其进行+1写回去变成了 2,如果我们希望线程B对其也进行+1操作后sum = 3,但是因为B线程获取到的是sum = 1,所以操作后写回去也是sum = 2 。为了解决类似的问题,引入了锁的概念——即在一个线程操作时进行锁定,暂时不给别的资源操作,直到其释放。
例子:使用synchronized进行同步
先看不加锁的情况:
class Total { private int sum; //共享资源 public Total() { this.sum = 0; } public int getSum() { return sum; } public void setSum(int sum) { this.sum = sum; } } class TotalTest implements Runnable { static final Total total = new Total(); @Override public void run() { for(int i = 0;i<1000;i++) { total.setSum(total.getSum()+1); } } } public class T_test { public static void main(String[] args) { TotalTest tt1 = new TotalTest(); /* * 开了两个线程进行模拟 ,如果输出的sum结果不等于2000 * 那么就说明出现了线程安全问题 */ Thread t1 = new Thread(tt1); Thread t2 = new Thread(tt1); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(tt1.total.getSum()); } }
输出: 1181
解决方案:在访问共享资源的代码块外加上同步锁
class TotalTest implements Runnable { static final Total total = new Total(); @Override public void run() { for(int i = 0;i<1000;i++) { synchronized (this) { //加入了实例锁 total.setSum(total.getSum()+1); } } } }
前面我们只实例化了一个线程实例,加实例锁可以解决线程同步问题。下面我们实例化两个线程对象
public class T_test { public static void main(String[] args) { TotalTest tt1 = new TotalTest(); TotalTest tt2 = new TotalTest(); /* * 开了两个线程进行模拟 ,如果输出的sum结果不等于2000 * 那么就说明出现了线程安全问题 */ Thread t1 = new Thread(tt1); Thread t2 = new Thread(tt2); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(tt1.total.getSum()); } }
这时输出:1970
解决方案 :加上类级锁
public void run() { for(int i = 0;i<1000;i++) { synchronized (TotalTest.class) { //加上了TotalTest类级锁 total.setSum(total.getSum()+1); } } }
输出:2000