源无极

导航

 

一、同步代码块锁this

(一)同步方法时

synchronized声明方法在有些情况下是有弊端的,比如A线程调用同步方法执行一段很长的一段时间,
则B线程一直在等待,这样使用synchronized代码块是对某一个对象进行加锁。
package com.it.po.thread05;

public class Task1 {
    private String getData1;
    private String getData2;
    synchronized public void doLongTimeTask(){
        try {
            System.out.println("线程:"+Thread.currentThread().getName()+" begin task");
            Thread.sleep(3000);
            System.out.println("执行长时间后获取数据返回。。。getData1 和getData2 ");
            System.out.println("线程:"+Thread.currentThread().getName()+" end task");
            getData1 = "获取数据返回  getData1";
            getData2 = "获取数据返回  getData2";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
package com.it.po.thread05;
public class MyThread01_1 extends Thread{
  private Task1 task1;
    public MyThread01_1(Task1 task1) {
        this.task1 = task1;
    }
    @Override
    public void run() {
        super.run();
        task1.doLongTimeTask();
    }
}
package com.it.po.thread05;
public class MyThread01_2 extends Thread{
  private Task1 task1;
    public MyThread01_2(Task1 task1) {
        this.task1 = task1;
    }
    @Override
    public void run() {
        super.run();
        task1.doLongTimeTask();
    }
}

 

package com.it.po.thread05;
import com.it.po.thread04.GetNum;
import com.it.po.thread04.MyThread01_1;
import com.it.po.thread04.MyThread01_2;
import com.it.po.thread04.MyThread9_1;
public class Run01 {
    public static void main(String[] args){
        Task1 task1 = new Task1();
        com.it.po.thread05.MyThread01_1 my1 = new com.it.po.thread05.MyThread01_1(task1);
        com.it.po.thread05.MyThread01_2 my2 = new com.it.po.thread05.MyThread01_2(task1);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

线程:A begin task
执行长时间后获取数据返回。。。
线程:A end task
线程:B begin task
执行长时间后获取数据返回。。。
线程:B end task

 (二)、同步代码块解决同步方法的弊端

package com.it.po.thread05;

public class Task2 {
    private String getData1;
    private String getData2;
     public void doLongTimeTask(){
         try {
             System.out.println("线程:"+Thread.currentThread().getName()+" begin task");
             Thread.sleep(3000);
             System.out.println("执行长时间后获取数据返回  getData1 和getData2.。。");
             System.out.println("线程:"+Thread.currentThread().getName()+" end task");
             synchronized (this) {
                 getData1 = Thread.currentThread().getName()+ " 获取数据  getData1";
                 getData2 = Thread.currentThread().getName()+" 获取数据  getData2";
                 System.out.println("getData1= "+getData1);
                 System.out.println("getData2= "+getData2);
             }

         } catch (InterruptedException e) {
             e.printStackTrace();
         }
    }

}

 

package com.it.po.thread05;
public class MyThread02_1 extends Thread{
  private Task2 task2;
    public MyThread02_1(Task2 task2) {
        this.task2 = task2;
    }
    @Override
    public void run() {
        super.run();
        task2.doLongTimeTask();
    }
}

 

package com.it.po.thread05;
public class MyThread02_2 extends Thread{
  private Task2 task2;
    public MyThread02_2(Task2 task2) {
        this.task2 = task2;
    }
    @Override
    public void run() {
        super.run();
        task2.doLongTimeTask();
    }
}

 

package com.it.po.thread05;

public class Run02 {
    public static void main(String[] args){
        Task2 task2 = new Task2();
        com.it.po.thread05.MyThread02_1 my1 = new com.it.po.thread05.MyThread02_1(task2);
        com.it.po.thread05.MyThread02_2 my2 = new com.it.po.thread05.MyThread02_2(task2);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}
线程:B begin task
线程:A begin task
执行长时间后获取数据返回  getData1 和getData2.。。
执行长时间后获取数据返回  getData1 和getData2.。。
线程:B end task
线程:A end task
getData1= B 获取数据  getData1
getData2= B 获取数据  getData2
getData1= A 获取数据  getData1
getData2= A 获取数据  getData2

 

结论1
一半异步一半同步

不在 synchronized执行就是异步,在 synchronized中执行就是同步,如上
红色部分异步,下方同步。

二、 synchronized(this)锁的是当前对象。
package com.it.po.thread05;

public class ObjectService {

  synchronized public void methodA(){
      System.out.println("brgin time= "+System.currentTimeMillis()+" methodA....");
      try {
          Thread.sleep(1000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println("end time= "+System.currentTimeMillis()+" methodA....");
  }

     public void methodB(){
      synchronized (this) {
          System.out.println("brgin time= " + System.currentTimeMillis() + " methodB....");
          System.out.println("end  time= " + System.currentTimeMillis() + " methodB....");
      }
    }

}

 

package com.it.po.thread05;
public class MyThread03_1 extends Thread{
  private ObjectService  objectService;
    public MyThread03_1( ObjectService  objectService) {
        this.objectService = objectService;
    }
    @Override
    public void run() {
        super.run();
        objectService.methodA();
    }
}

 

package com.it.po.thread05;
public class MyThread03_2 extends Thread{
  private ObjectService  objectService;
    public MyThread03_2(ObjectService  objectService) {
        this.objectService = objectService;
    }
    @Override
    public void run() {
        super.run();
        objectService.methodB();
    }
}

 

package com.it.po.thread05;
public class Run03 {
    public static void main(String[] args){
        ObjectService service = new ObjectService();
        MyThread03_1 my1 = new MyThread03_1(service);
        MyThread03_2 my2 = new MyThread03_2(service);
        my1.start();
        my2.start();
    }

}

 

brgin time= 1573870649146 methodA....
end time= 1573870650147 methodA....
brgin time= 1573870650148 methodB....
end  time= 1573870650148 methodB....

 

三、任意对象作为对象对象监视器。

1、synchronized同步方法
1)对其他synchronized 同步方法或是 synchronized(this)同步代码块调用呈阻塞状态。
2)同一时间,只有一个线程可以执行 synchronized 同步方法中的代码。
2、synchronized(this)同步代码块
1)对其他synchronized 同步方法或是 synchronized(this)同步代码块调用呈阻塞状态。
2)同一时间,只有一个线程可以执行 synchronized 同步代码块中的代码。
此外
监视器除了可以用this,还可以用任意对象。
这个任意对象大多数是实例变量及方法参数
(一)、实例变量作为对象监视器
package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class Service1 {
    private String username;
    private String password;
    private List<String> list = new ArrayList<>() ;
   // private Object object=new Object();
   // private String po= new String();
  public  void methodA( String username, String password){
      try {
          synchronized (list){
              System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。");
              this.username=username;
              Thread.sleep(2000);
              this.password=password;
              System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。");
          }

      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
} 

 

package com.it.po.thread05;
public class MyThread04_1 extends Thread{
    private Service1 service1;
    public MyThread04_1(Service1 service1) {
        this.service1 = service1;
    }
    @Override
    public void run() {
        super.run();
       //  new Service1().methodA("a","aa"); 
        service1.methodA("a","aa");
    }
}

 

package com.it.po.thread05;
public class MyThread04_2 extends Thread{
    private Service1 service1;
    public MyThread04_2(Service1 service1) {
        this.service1 = service1;
    }
    @Override
    public void run() {
        super.run();
       // new Service1().methodA("b","bb");
        service1.methodA("b","bb");
    }
}

 

package com.it.po.thread05;

public class Run04 {
    public static void main(String[] args){
        Service1 service1 = new Service1();
        MyThread04_1 my1 = new MyThread04_1(service1);
        MyThread04_2 my2 = new MyThread04_2(service1);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

线程 A 进来了。。
线程 A 出去了。。
线程 B 进来了。。
线程 B 出去了。。

 

注意:此处虽然 synchronized (list) ,但是Service1必须是一个对象,否则就是多个list,无法锁住的。
此处,还可以将 private List<String> list = new ArrayList<>() ;换成如下。
private Object object=new Object();
private String po= new String();
(二)、特殊的情况字符常量作为监视器时
上面的例子修改如下
package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class Service1 {
    private String username;
    private String password;
    private List<String> list = new ArrayList<>() ;
   // private Object object=new Object();
   // private String po= new String();
   private String lan="pp";
  public  void methodA( String username, String password){
      try {
          synchronized (lan){
              System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。");
              this.username=username;
              Thread.sleep(2000);
              this.password=password;
              System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。");
          }

      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

 

package com.it.po.thread05;

public class Run04 {
    public static void main(String[] args){
        Service1 service1 = new Service1();
        Service1 service2 = new Service1();
        MyThread04_1 my1 = new MyThread04_1(service1);
        MyThread04_2 my2 = new MyThread04_2(service2);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

线程 B 进来了。。
线程 B 出去了。。
线程 A 进来了。。
线程 A 出去了。。

 

虽然两个对象service1 和service2是不一样的,但是字符常量lan="pp" 是一份,也是可以锁住的。
(三)对象监视器在方法内
package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class Service2 {
    private String username;
    private String password;
  public  void methodA( String username, String password){
       List<String> list = new ArrayList<>() ;
      //  Object object=new Object();
      //  String po= new String();
      try {
          synchronized (list){
              System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。");
              this.username=username;
              Thread.sleep(2000);
              this.password=password;
              System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。");
          }

      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

 

package com.it.po.thread05;
public class MyThread05_1 extends Thread{
    private Service2 service2;
    public MyThread05_1(Service2 service2) {
        this.service2 = service2;
    }
    @Override
    public void run() {
        super.run();
       //  new Service1().methodA("a","aa");
        service2.methodA("a","aa");
    }
}
package com.it.po.thread05;
public class MyThread05_2 extends Thread{
    private Service2 service2;
    public MyThread05_2(Service2 service2) {
        this.service2 = service2;
    }
    @Override
    public void run() {
        super.run();
       //  new Service1().methodA("a","aa");
        service2.methodA("a","aa");
    }
}

 

package com.it.po.thread05;
public class Run05 {
    public static void main(String[] args){
        Service2 service2 = new Service2();
        MyThread05_1 my1 = new MyThread05_1(service2);
        MyThread05_2 my2 = new MyThread05_2(service2);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

线程 A 进来了。。
线程 B 进来了。。
线程 A 出去了。。
线程 B 出去了。。

 

注意:方法内是线程安全的,多线程互不干扰

(四)对象监视器在方法内时实现同步效果 
修改如下
package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class Service2 {
    private String username;
    private String password;
  public  void methodA( String username, String password){
      // List<String> list = new ArrayList<>() ;
      // Object object=new Object();
       //String po= new String();
       String lu= "pp";
      try {
          synchronized (lu){
              System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。");
              this.username=username;
              Thread.sleep(2000);
              this.password=password;
              System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。");
          }

      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

 

线程 B 进来了。。
线程 B 出去了。。
线程 A 进来了。。
线程 A 出去了。。

 对象监视器不同运行的结果就是异步的。

(五)循环添加数据可能出现的脏读
package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class MyList1 {
private List list =new ArrayList();
  synchronized  public void add(String name){
     System.out.println("线程 :"+Thread.currentThread().getName()+" 进入执行了add方法");
      list.add(name);
      System.out.println("线程 :"+Thread.currentThread().getName()+" 出去add方法");
  }
    synchronized  public int getSize(){
        System.out.println("线程 :"+Thread.currentThread().getName()+" 进入 执行了getSize方法");
        int size = list.size();
        System.out.println("线程 :"+Thread.currentThread().getName()+" 出去getSize方法");
        return  size;
    }
}

 

 
package com.it.po.thread05;
public class MyThread06_1 extends Thread{
    private MyList1 myList1;

    public MyThread06_1(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
        for(int i=0;i<100;i++){
            myList1.add("MyThread06_1 "+i);
        }
    }
}

 

package com.it.po.thread05;
public class MyThread06_2 extends Thread{
    private MyList1 myList1;
    public MyThread06_2(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
        for(int i=0;i<100;i++){
            myList1.add("MyThread06_2 "+i);
        }
    }
}

 

package com.it.po.thread05;

public class Run06 {
    public static void main(String[] args){
        MyList1 myList1 = new MyList1();
        MyThread06_1 my1 = new MyThread06_1(myList1);
        MyThread06_2 my2 = new MyThread06_2(myList1);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

 

 同步块的代码虽然是同步打印的,但是线程A和线程B确是异步的,这就有可能出现脏读的情况。

(六)重现脏读的情况

package com.it.po.thread05;

import java.util.ArrayList;
import java.util.List;

public class MyList2 {
    private List list =new ArrayList();
    synchronized  public void add(String name){
        System.out.println("线程 :"+Thread.currentThread().getName()+" 进入执行了add方法");
        list.add(name);
        System.out.println("线程 :"+Thread.currentThread().getName()+" 出去add方法");
    }
    synchronized  public int getSize(){
        System.out.println("线程 :"+Thread.currentThread().getName()+" 进入 执行了getSize方法");
        int size = list.size();
        System.out.println("线程 :"+Thread.currentThread().getName()+" 出去getSize方法");
        return  size;
    }

}

 

package com.it.po.thread05;
public class MyService1 {
    public MyList2 addServiceMethod(MyList2 list2,String data) {
        try {
             if(list2.getSize()<1){
                 Thread.sleep(2000);//模拟从远端获取数据话费2s
                 list2.add(data);
             }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return list2;
    }

}

 

package com.it.po.thread05;
public class MyThread07_1 extends Thread{
    private MyList2 myList2;
    public MyThread07_1(MyList2 myList2) {
        this.myList2 = myList2;
    }
    @Override
    public void run() {
        super.run();
        MyService1 myService1 = new MyService1();
        myService1.addServiceMethod(myList2,"A");
    }
}

 

package com.it.po.thread05;
public class MyThread07_2 extends Thread{
    private MyList2 myList2;
    public MyThread07_2(MyList2 myList2) {
        this.myList2 = myList2;
    }
    @Override
    public void run() {
        super.run();
        MyService1 myService1 = new MyService1();
        myService1.addServiceMethod(myList2,"B");
    }
}

 

package com.it.po.thread05;

public class Run07 {
    public static void main(String[] args){
        try {
            MyList2 myList2 = new MyList2();
            MyThread07_1 my1 = new MyThread07_1(myList2);
            MyThread07_2 my2 = new MyThread07_2(myList2);
            my1.setName("A");
            my2.setName("B");
            my1.start();
            my2.start();
            Thread.sleep(3000);
            System.out.println("myList2大小:"+myList2.getSize());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

线程 :B 进入 执行了getSize方法
线程 :B 出去getSize方法
线程 :A 进入 执行了getSize方法
线程 :A 出去getSize方法
线程 :B 进入执行了add方法
线程 :B 出去add方法
线程 :A 进入执行了add方法
线程 :A 出去add方法
线程 :main 进入 执行了getSize方法
线程 :main 出去getSize方法
myList2大小:2

 

两个线程停留在if判断那里

避免脏读
package com.it.po.thread05;
public class MyService1 {

    public MyList2 addServiceMethod(MyList2 list2,String data) {
        synchronized (list2) {
            try {
                if (list2.getSize() < 1) {
                    Thread.sleep(2000);//模拟从远端获取数据话费2s
                    list2.add(data);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            return list2;

    }

}

 

线程 :B 进入 执行了getSize方法
线程 :B 出去getSize方法
线程 :B 进入执行了add方法
线程 :B 出去add方法
线程 :A 进入 执行了getSize方法
线程 :A 出去getSize方法
线程 :main 进入 执行了getSize方法
线程 :main 出去getSize方法
myList2大小:1

 

总结:
1.当多个线程同时执行 synchronized同步代码块时,呈同步效果。
2.当其他线程执行X对象中 synchronized同步方法时呈同步效果。
package com.it.po.thread05;
public class MyObject {
synchronized public void myObjectMethod(){
    try {
        System.out.println("线程 "+Thread.currentThread().getName()+" 进入myObjectMethod。。  ");
        Thread.sleep(2000);
        System.out.println("线程 "+Thread.currentThread().getName()+" 走出myObjectMethod。。。  ");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

 

package com.it.po.thread05;

public class Service3 {
  public void methodA(MyObject myObject){
      try {
          synchronized (myObject) {
              System.out.println("线程 " + Thread.currentThread().getName() + " 进入 methodA。。  ");
              Thread.sleep(2000);
              System.out.println("线程 " + Thread.currentThread().getName() + "走出 methodA。。。  ");
          }
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }

}

 

package com.it.po.thread05;
public class MyThread08_1 extends Thread{
  private MyObject myObject;
  private Service3 service3;
    public MyThread08_1(MyObject myObject, Service3 service3) {
        this.myObject = myObject;
        this.service3 = service3;
    }
    @Override
    public void run() {
        super.run();
        myObject.myObjectMethod();
    }
}

 

package com.it.po.thread05;
public class MyThread08_2 extends Thread{
  private MyObject myObject;
  private Service3 service3;
    public MyThread08_2(MyObject myObject, Service3 service3) {
        this.myObject = myObject;
        this.service3 = service3;
    }
    @Override
    public void run() {
        super.run();
        service3.methodA(myObject);
    }
}

 

package com.it.po.thread05;

public class Run08 {
    public static void main(String[] args){
        try {
            MyObject myObject = new MyObject();
            Service3 service3 = new Service3();
            MyThread08_1 my1 = new MyThread08_1(myObject,service3);
            MyThread08_2 my2= new MyThread08_2(myObject,service3);
            my1.setName("A");
            my2.setName("B");
            my1.start();
            my2.start();
            Thread.sleep(3000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

线程 B 进入 methodA。。  
线程 B走出 methodA。。。  
线程 A 进入myObjectMethod。。  
线程 A 走出myObjectMethod。。。  

 

3.当其他线程执行X对象方法里面的 synchronized(this)代码块时也呈同步效果(上面例子修改如下)
package com.it.po.thread05;
public class MyObject {
   public void myObjectMethod() {
    try {
        synchronized (this) {
            System.out.println("线程 " + Thread.currentThread().getName() + " 进入myObjectMethod。。  ");
            Thread.sleep(2000);
            System.out.println("线程 " + Thread.currentThread().getName() + " 走出myObjectMethod。。。  ");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

 

线程 A 进入myObjectMethod。。  
线程 A 走出myObjectMethod。。。  
线程 B 进入 methodA。。  
线程 B走出 methodA。。。 

 





 


posted on 2019-11-15 23:58  源无极  阅读(420)  评论(0编辑  收藏  举报