源无极

导航

 

一、方法内的变量是线程安全

非线程安全是对于 “实例变量而言的”,对于方法内的变量,不存在线程安全问题

package com.it.po.thread04;

public class GetNum {
 public void getNum(String username){
   try {
       int num=0;
       if("a".equals(username)){
         num=100;
        Thread.sleep(1000);
        }else {
         num=200;
       }
       System.out.println("名称:username= "+username+" num= "+num);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

}

 

package com.it.po.thread04;
public class MyThread01_2 extends Thread {
 private  GetNum getNum;
    public MyThread01_2(GetNum getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("b");
    }
}

 

package com.it.po.thread04;
public class MyThread01_1 extends Thread {
 private  GetNum getNum;
    public MyThread01_1(GetNum getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("a");
    }
}

 

package com.it.po.thread04;

public class Run01 {
    public static void main(String[] args){
        GetNum getNum = new GetNum();
        MyThread01_1 my1 = new MyThread01_1(getNum);
        MyThread01_2 my2 = new MyThread01_2(getNum);
        my1.start();
        my2.start();
    }

}

 

二、实例变量非线程安全

(一)两个线程访问没有同步方法的实例变量

如果多个线程访问一个对象的实例变量,则可能出现线程安全问题

package com.it.po.thread04;
public class GetNum2 {
    private  int num=0;
 public void getNum(String username){
   try {
       if("a".equals(username)){
         num=100;
        Thread.sleep(2000);
        }else {
         num=200;
       }
       System.out.println("名称:username= "+username+" num= "+num);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

}

 

package com.it.po.thread04;
public class MyThread02_1 extends Thread {
 private  GetNum2 getNum;
    public MyThread02_1(GetNum2 getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("a");
    }
}

 

package com.it.po.thread04;
public class MyThread02_2 extends Thread {
 private  GetNum2 getNum;
    public MyThread02_2(GetNum2 getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("b");
    }
}

 

package com.it.po.thread04;
public class Run02 {
    public static void main(String[] args){
        GetNum2 getNum = new GetNum2();
        MyThread02_1 my1 = new MyThread02_1(getNum);
        MyThread02_2 my2 = new MyThread02_2(getNum);
        my1.start();
        my2.start();
    }

}

 

GetNum2 中只有一个变量会被覆盖,多个变量会出现交叉的情况。

(二)、两个线程访问同步方法的实例变量(上面的例子中GetNum2 方法添加synchronized 

package com.it.po.thread04;
public class GetNum2 {
    private  int num=0;
 synchronized  public void getNum(String username){
   try {
       if("a".equals(username)){
         num=100;
        Thread.sleep(2000);
        }else {
         num=200;
       }
       System.out.println("名称:username= "+username+" num= "+num);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

}

 

名称:username= b num= 200
名称:username= a num= 100

 

这里情况是线程安全的。

三、多个对象多个锁

 

package com.it.po.thread04;
public class GetNum3 {
    private  int num=0;
  synchronized public void getNum(String username){
   try {
       if("a".equals(username)){
         num=100;
         System.out.println("a set over...");
        Thread.sleep(2000);
        }else {
         num=200;
           System.out.println("b set over...");
       }
       System.out.println("名称:username= "+username+" num= "+num);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

}
package com.it.po.thread04;

public class MyThread03_1 extends Thread {
 private  GetNum3 getNum;
    public MyThread03_1(GetNum3 getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("a");
    }
}
package com.it.po.thread04;
public class MyThread03_2 extends Thread {
 private  GetNum3 getNum;
    public MyThread03_2(GetNum3 getNum) {
        this.getNum = getNum;
    }
    @Override
    public void run() {
        super.run();
        getNum.getNum("b");
    }
}
package com.it.po.thread04;
public class Run03 {
    public static void main(String[] args){
        GetNum3 getNum1 = new GetNum3();
        GetNum3 getNum2 = new GetNum3();
        MyThread03_1 my1 = new MyThread03_1(getNum1);
        MyThread03_2 my2 = new MyThread03_2(getNum2);
        my1.start();
        my2.start();
    }

}

 

两个线程分别访问同一个类的不同实例的相同名称的方法,效果却是异步方式运行的,

上面创建了两个业务对象,产生了两个锁。

注意

1. synchronized取得的锁都是对象锁,而不是把一段代码或是方法的当成锁。

2.一般哪个线程先执行带synchronized的方法,哪个线程就持有该对象作为锁,这只是对于该对象

只有一个锁的情况下。

同步的单词:synchronized

异步的单词:asynchronized.

四、synchronized方法与锁对象

证明synchronized锁的是对象

package com.it.po.thread04;

public class MyObject {
  public void methodA(){
     try {
         System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。。");
         Thread.sleep(3000);
         System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。。");
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
}

}
package com.it.po.thread04;

public class MyThread04_1 extends Thread {
   private MyObject myObject;
    public MyThread04_1(MyObject myObject) {
        this.myObject = myObject;
    }
    @Override
    public void run() {
        super.run();
        myObject.methodA();
    }
}
package com.it.po.thread04;

public class MyThread04_2 extends Thread {
   private MyObject myObject;
    public MyThread04_2(MyObject myObject) {
        this.myObject = myObject;
    }
    @Override
    public void run() {
        super.run();
        myObject.methodA();
    }
}
package com.it.po.thread04;
public class Run04 {
    public static void main(String[] args){
        MyObject myObject = new MyObject();
        MyThread04_1 my1 = new MyThread04_1(myObject);
        MyThread04_2 my2 = new MyThread04_2(myObject);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

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

修改 MyObject  在方法前加 synchronized 

package com.it.po.thread04;

public class MyObject {
 synchronized public void methodA(){
     try {
         System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。。");
         Thread.sleep(3000);
         System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。。");
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
}

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

 

分析:资源对象只有一个,方法有 synchronized ,多线程需要排队。

牢牢记住只有共享资源才需要同步化(比如加synchronized 

如果不是共享资源,那么根本没有同步的必要

(二)一个类存在非同步方法情况

package com.it.po.thread04;

public class MyObject1 {
 synchronized public void methodA(){
     try {
         System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 进来了。。。");
         Thread.sleep(3000);
         System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 出去了。。。");
         System.out.println("methodA time= "+System.currentTimeMillis());
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
}

     public void methodB(){
        try {
            System.out.println("methodB 线程:"+Thread.currentThread().getName()+" 进来了。。time= "+
                    System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println(" methodB 线程:"+Thread.currentThread().getName()+" 出去了。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
package com.it.po.thread04;

public class MyThread05_1 extends Thread {
   private MyObject1 myObject;
    public MyThread05_1(MyObject1 myObject) {
        this.myObject = myObject;
    }
    @Override
    public void run() {
        super.run();
        myObject.methodA();
    }
}
package com.it.po.thread04;
public class MyThread05_2 extends Thread {
   private MyObject1 myObject;
    public MyThread05_2(MyObject1 myObject) {
        this.myObject = myObject;
    }
    @Override
    public void run() {
        super.run();
        myObject.methodB();
    }
}
package com.it.po.thread04;
public class Run05 {
    public static void main(String[] args){
        MyObject1 myObject = new MyObject1();
        MyThread05_1 my1 = new MyThread05_1(myObject);
        MyThread05_2 my2 = new MyThread05_2(myObject);
        my1.setName("C");
        my2.setName("D");
        my1.start();
        my2.start();
    }

}
methodB 线程:D 进来了。。time= 1573568524394
methodA 线程:C 进来了。。。
 methodB 线程:D 出去了。。。
methodA 线程:C 出去了。。。
methodA time= 1573568527395

 结论,方法B虽然不是同步方法,但是线程可以不用排队。线程D异步调用方法B

methodB 方法加上  synchronized
package com.it.po.thread04;

public class MyObject1 {
 synchronized public void methodA(){
     try {
         System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 进来了。。。");
         Thread.sleep(3000);
         System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 出去了。。。");
         System.out.println("methodA time= "+System.currentTimeMillis());
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
}

    synchronized public void methodB(){
        try {
            System.out.println("methodB 线程:"+Thread.currentThread().getName()+" 进来了。。time= "+
                    System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println(" methodB 线程:"+Thread.currentThread().getName()+" 出去了。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
methodA 线程:C 进来了。。。
methodA 线程:C 出去了。。。
methodA time= 1573568844110
methodB 线程:D 进来了。。time= 1573568844110
 methodB 线程:D 出去了。

 (三)synchronized  锁重入

 当一个线程得到一个对象锁之后,再次请求此锁是可以再次得到该对象的锁的 。

 在synchronized  的方法/块的内部调用本类的其他synchronized  是永远得到锁的。

 

package com.it.po.thread04;

public class Service {
  synchronized  public void service1(){
      System.out.println("service1...");
      service2();
  }
    synchronized  public void service2(){
        System.out.println("service2...");
        service3();
    }
    synchronized  public void service3(){
        System.out.println("service3...");

    }

}
package com.it.po.thread04;

public class MyThread06 extends Thread {
    @Override
    public void run() {
        super.run();
        Service service = new Service();
        service.service1();
    }
}
package com.it.po.thread04;

public class Run06 {
    public static void main(String[] args){
        MyThread06 myThread06 = new MyThread06();
        myThread06.start();
    }

}

 

 (四)synchronized  锁重入支持在父子类继承的环境

 

package com.it.po.thread04;

public class Main {
  public int i = 10;
 synchronized  public void operationMainMethod(){
     try {
         i--;
         System.out.println("main print i= "+i);
         Thread.sleep(100);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }

 }
}
package com.it.po.thread04;
public class Sub extends Main {
  synchronized  public void operationSubMethod()  {
         try {
             while (i>0) {
                 i--;
                 System.out.println("Sub print i= " + i);
                 Thread.sleep(100);
                 this.operationMainMethod();//调用父类的
             }
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }

}
package com.it.po.thread04;
public class MyThread7 extends Thread {
    @Override
    public void run() {
        super.run();
        Sub sub = new Sub();
        sub.operationSubMethod();
    }
}
package com.it.po.thread04;
public class Run07 {
    public static void main(String[] args){
        MyThread7 myThread07 = new MyThread7();
        myThread07.start();
    }

}
Sub print i= 9
main print i= 8
Sub print i= 7
main print i= 6
Sub print i= 5
main print i= 4
Sub print i= 3
main print i= 2
Sub print i= 1
main print i= 0

 

(五)、出现异常锁自动释放。

package com.it.po.thread04;
public class Service2 {
    synchronized  public void testmethod(){
        System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。");
        if("A".equals(Thread.currentThread().getName())){
            System.out.println("线程A出现遇到异常。。。");
            int i=1/0;
        }
        System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。");
    }

}
package com.it.po.thread04;
public class MyThread8_1 extends Thread {
    private Service2 service2;
    public MyThread8_1(Service2 service2) {
        this.service2 = service2;
    }
    @Override
    public void run() {
        super.run();
        service2.testmethod();
    }
}
package com.it.po.thread04;
public class MyThread8_2 extends Thread {
    private Service2 service2;
    public MyThread8_2(Service2 service2) {
        this.service2 = service2;
    }
    @Override
    public void run() {
        super.run();
        service2.testmethod();
    }
}
package com.it.po.thread04;
public class Run08 {
    public static void main(String[] args){
        Service2 service2 = new Service2();
        MyThread8_1 my1 = new MyThread8_1(service2);
        MyThread8_2 my2 = new MyThread8_2(service2);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}
线程:A 进来了。。
线程A出现遇到异常。。。
Exception in thread "A" java.lang.ArithmeticException: / by zero
    at com.it.po.thread04.Service2.testmethod(Service2.java:8)
    at com.it.po.thread04.MyThread8_1.run(MyThread8_1.java:11)
线程:B 进来了。。
线程:B 出去了。。

 

注意:这种情况有给实例变量赋值,很有可能出现脏读情况。

 (六)、同步不具有继承性

 

package com.it.po.thread04;

public class Main2 {

  synchronized  public  void serviceMethod(){
      try {
          System.out.println("Main2 线程:"+Thread.currentThread().getName()+" 进来了 " +
                  "time= "+System.currentTimeMillis());
          Thread.sleep(2000);
          System.out.println("Main2 线程:"+Thread.currentThread().getName()+" 出去了 " +
                  "time= "+System.currentTimeMillis());
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}
package com.it.po.thread04;

public class Sub2 extends Main2{
   @Override
     public  void serviceMethod(){
      try {
          System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 进来了 " +
                  "time= "+System.currentTimeMillis());
          Thread.sleep(2000);
          System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 出去了 " +
                  "time= "+System.currentTimeMillis());
          super.serviceMethod();
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}
package com.it.po.thread04;
public class MyThread9_1 extends Thread {
    private Sub2  sub2;
    public MyThread9_1(Sub2  sub2) {
        this.sub2 = sub2;
    }
    @Override
    public void run() {
        super.run();
        sub2.serviceMethod();
    }
}
package com.it.po.thread04;
public class MyThread9_2 extends Thread {
    private Sub2  sub2;
    public MyThread9_2(Sub2  sub2) {
        this.sub2 = sub2;
    }
    @Override
    public void run() {
        super.run();
        sub2.serviceMethod();
    }
}
package com.it.po.thread04;
public class Run09 {
    public static void main(String[] args){
        Sub2 sub2 = new Sub2();
        MyThread9_1 my1 = new MyThread9_1(sub2);
        MyThread9_2 my2 = new MyThread9_2(sub2);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}
Sub2 线程:A 进来了 time= 1573830321129
Sub2 线程:B 进来了 time= 1573830321129
Sub2 线程:B 出去了 time= 1573830323130
Sub2 线程:A 出去了 time= 1573830323130
Main2 线程:B 进来了 time= 1573830323130
Main2 线程:B 出去了 time= 1573830325130
Main2 线程:A 进来了 time= 1573830325130
Main2 线程:A 出去了 time= 1573830327130

 

修改成同步的

package com.it.po.thread04;

public class Sub2 extends Main2{
   @Override
     synchronized  public  void serviceMethod(){
      try {
          System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 进来了 " +
                  "time= "+System.currentTimeMillis());
          Thread.sleep(2000);
          System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 出去了 " +
                  "time= "+System.currentTimeMillis());
          super.serviceMethod();
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

 

 

Sub2 线程:A 进来了 time= 1573830438039
Sub2 线程:A 出去了 time= 1573830440039
Main2 线程:A 进来了 time= 1573830440039
Main2 线程:A 出去了 time= 1573830442039
Sub2 线程:B 进来了 time= 1573830442039
Sub2 线程:B 出去了 time= 1573830444039
Main2 线程:B 进来了 time= 1573830444039
Main2 线程:B 出去了 time= 1573830446040

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2019-11-11 22:40  源无极  阅读(199)  评论(0)    收藏  举报