> 文档中心 > 浅析多线程访问同一资源的问题

浅析多线程访问同一资源的问题


锁的概念:

多个线程在对同一个资源进行访问时要上锁

synchronized加在静态方法上和在代码中sychronized这个类是等价的:
加在非静态方法上和在代码中sychronized(this)是等价的
synchronized static void m() == sychronized (T.class) // 类锁
synchronized void m() == sychronized(this) // 对象

重点:
1、程序之中如果出现异常,默认情况下锁会被释放
底层源码有两个monitorexit,一个是锁正常情况下的退出,一个是异常情况下的退出

2、synchronized锁作用的是对象不是代码

synchronized锁升级的概念:
当只有一个线程访问时,在synchronized的对象(object)的markword上记录这个线程的ID,此时单个线程调用并没有加锁;如果有其他线程争用时会升级为轻量级锁,也叫自选锁,线程在那转圈访问锁是否被释放了,如果默认旋转一定时间(10次)还没有释放的话,锁就会升级为重量级锁,重量级锁时这个线程就会有一个队列,就会将这些线程放到等待队列中,等锁释放后根据队列中的顺序一次获得资源执行。

注:
加锁代码执行时间短,线程数少,用轻量级锁
执行时间长,线程数多,用重量级锁

synchronized(object),object不要用基础类型的,如String,Integer,Long等:

原因是java的自动封箱和拆箱在作怪,Integer,Long:当执行i++,实际上就是i=new Integer(i+1),所以执行完i++后i已经不是以前的那个对象了,同步块自然也就失效了
String: String定义的变量会放在常量池中,如果多个线程定义的String变量的值相等, 指向的地址是一致的,这样两个线程指向的实际就是同一个对象,这时锁就无效了。所以严格意义上来讲是不要用String常量来作为锁对象,但为了规避风险,最好直接就不要用
String作为锁对象
注:所以锁对象不要用基础的数据类型!!!

例子:如果有两以上的线程同时访问同一个共享资源,可能造成线程冲突,线程冲突会造成数据丢失、重复等严重问题。
以下通过两个线程同时访问同一个类,来表现线程冲突,如果产生冲突便会打印输出。

public class TestDemo {String name;public static void main(String[] args) {TestDemo td =new TestDemo();Thread t = new Thread() {    @Override    public void run() {    try {    while(true) {    Thread.sleep(500);    td.aa("ls");    }    }catch (InterruptedException e) {e.printStackTrace();}    }    }; t.start();    Thread f = new Thread() {    public void run() {  try {      while(true) {      Thread.sleep(500);      td.aa("aas");      }  } catch (InterruptedException e) {      // TODO Auto-generated catch block      e.printStackTrace();  }     }      }; f.start();}public void aa(String name) {this.name=name;eq(name);}private void eq(String name) {if(!(name==this.name)) {System.out.println(this.name+" "+name);}}}

日志打印:

ls aasaas lsaas lsaas lsls aasaas lsaas ls

解决方法:可以使用synchronized关键字让线程同步。

线程的冲突:
在多个线程访问同一个对象的同一个属性时,才会出现线程冲突,线程冲突会造成数据丢失、重复等严重问题。为了避免这个问题可以加synchronized关键字让线程同步。
例子:

public class Test {   String name;   public static void main(String[] args) {final Test ts=new Test();final Test ts2=new Test();final Thread t=new Thread(){    @Override    public  void run(){ while (true){     try {  ts.setName("张三");  Thread.sleep(1000);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }};t.start();final Thread t2=new Thread(){    @Override    public  void run(){ while (true){     try {  ts2.setName("李四");  Thread.sleep(1000);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }};t2.start();   }  /*   1,放在函数中锁住整个函数,只有前一个线程执行完下一个函数才能访问      *    2,放在代码块中,锁住需要共享的资源,推荐使用      */   public /*synchronized*/  void setName(String name){// 放在函数中锁住整个函数,只有前一个线程执行完下一个函数才能访问       // synchronized(this) {//2,放在代码块中,锁住需要共享的资源,推荐使用     this.name = name;    eqName(name);      // }   }   public void eqName(String name){if(name!=this.name)    System.out.println(name+" "+this.name);   }}