多线程:2.对象及变量的并发访问

2016-08-31  本文已影响18人  81bad73e9053

《多线程编程核心技术》笔记

1synchronized同步方法

1.1方法中的变量呈线程安全状态

方法中的变量不存在线程安全问题,永远是线程安全的,这是方法内部的变量是私有的特性决定的

public class HasselePrivateNum {
    public void add(String username){
        try{
            int num = 0;
            if(username.equals("a")){
                num = 100;
                System.out.println("a set num");
                Thread.sleep(2000);
            }else{
                num = 200;
                System.out.println("b set num");
            }
            System.out.println("username= "+username+"  num="+num);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread{ 
    private HasselePrivateNum numRef;
    public ThreadA(HasselePrivateNum numRef){
        super();
        this.numRef = numRef;
    }
     @Override
    public void run() { 
         super.run();
         numRef.add("a");
    } 
}

public class ThreadB extends Thread { 
    private HasselePrivateNum numRef;
    public ThreadB(HasselePrivateNum numRef){
        super();
        this.numRef = numRef;
    }
     @Override
    public void run() { 
         super.run();
         numRef.add("b");
    } 
}

public class Run { 
    public static void main(String[] args) { 
        HasselePrivateNum numRef = new HasselePrivateNum();
        ThreadA threadA = new ThreadA(numRef);
        ThreadB threadB = new ThreadB(numRef);
        threadA.start();
        threadB.start();
    } 
}
运行结果:
a set num
b set num
username= b  num=200
username= a  num=100

1.2实例变量非线程安全

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


public class HasselePrivateNum {
    private int num = 0;
    public void add(String username){
        try{ 
            if(username.equals("a")){
                num = 100;
                System.out.println("a set num");
                Thread.sleep(2000);
            }else{
                num = 200;
                System.out.println("b set num");
            }
            System.out.println("username= "+username+"  num="+num);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread{ 
    private HasselePrivateNum numRef;
    public ThreadA(HasselePrivateNum numRef){
        super();
        this.numRef = numRef;
    }
     @Override
    public void run() { 
         super.run();
         numRef.add("a");
    } 
}

public class ThreadB extends Thread { 
    private HasselePrivateNum numRef;
    public ThreadB(HasselePrivateNum numRef){
        super();
        this.numRef = numRef;
    }
     @Override
    public void run() { 
         super.run();
         numRef.add("b");
    } 
}

public class Run { 
    public static void main(String[] args) { 
        HasselePrivateNum numRef = new HasselePrivateNum();
        ThreadA threadA = new ThreadA(numRef);
        ThreadB threadB = new ThreadB(numRef);
        threadA.start();
        threadB.start();
    } 
}
运行结果:
a set num
b set num
username= b  num=200
username= a  num=200 

同步处理


public class HasselePrivateNum {
    private int num = 0;
    public synchronized void add(String username){
        try{ 
            if(username.equals("a")){
                num = 100;
                System.out.println("a set num");
                Thread.sleep(2000);
            }else{
                num = 200;
                System.out.println("b set num");
            }
            System.out.println("username= "+username+"  num="+num);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
运行结果:
a set num
username= a  num=100
b set num
username= b  num=200

1.3多个对象多个锁

关键字synchronized取得的锁都是对象锁,而不是把一段方法或者代码当成锁,多个线程访问多个对象,jvm会创建多个锁。

public class Run { 
    public static void main(String[] args) { 
        HasselePrivateNum numRefa = new HasselePrivateNum();
        HasselePrivateNum numRefb = new HasselePrivateNum();
        ThreadA threadA = new ThreadA(numRefa);
        ThreadB threadB = new ThreadB(numRefb);
        threadA.start();
        threadB.start();
    } 
}
运行结果:
a set num
b set num
username= b  num=200
username= a  num=100

1.4synchronized方法和锁对象

public class MyObject {
    public void method(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin method");
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread{ 
    private MyObject obj;
    public ThreadA(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.method();
    } 
}

public class ThreadB extends Thread { 
    private MyObject obj;
    public ThreadB(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.method();
    } 
}

public class Run { 
    public static void main(String[] args) { 
        MyObject obj = new MyObject();
        ThreadA threadA = new ThreadA(obj);
        ThreadB threadB = new ThreadB(obj);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    } 
}
运行结果:
A begin method
B begin method
end
end

两个线程操作一个对象
调用synchronized方法一定是排队运行的,只有共享资源的读写才是需要同步的,如果不是共享资源,就没有同步的必要。

public class MyObject {
    public synchronized void method(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin method");
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
运行结果:
A begin method
end
B begin method
end

一个对象中有两个方法的情况

结论:

A线程持有myobj的对象锁,B线程可以以异步的方式调用obj的非synchronized方法
A线程持有myojb对象锁,B线程如果这时候调用myobj的synchronized类型的方法则需要等待,也就是同步

public class MyObject {
    public synchronized void methodA(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin methodA");
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+"methodA end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public synchronized void methodB(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin methodB");
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+"methodB end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread{ 
    private MyObject obj;
    public ThreadA(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.methodA();
    } 
}

public class ThreadB extends Thread { 
    private MyObject obj;
    public ThreadB(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.methodB();
    } 
}

public class Run { 
    public static void main(String[] args) { 
        MyObject obj = new MyObject();
        ThreadA threadA = new ThreadA(obj);
        ThreadB threadB = new ThreadB(obj);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    } 
}
运行结果:
A begin methodA
AmethodA end
B begin methodB
BmethodB end
public class MyObject {
    public synchronized void methodA(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin methodA");
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+"methodA end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public  void methodB(){
        try {
            System.out.println(Thread.currentThread().getName()+" begin methodB");
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+"methodB end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread{ 
    private MyObject obj;
    public ThreadA(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.methodA();
    } 
}

public class ThreadB extends Thread { 
    private MyObject obj;
    public ThreadB(MyObject obj){
        super();
        this.obj = obj;
    }
     @Override
    public void run() { 
         super.run();
         obj.methodB();
    } 
}

public class Run { 
    public static void main(String[] args) { 
        MyObject obj = new MyObject();
        ThreadA threadA = new ThreadA(obj);
        ThreadB threadB = new ThreadB(obj);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    } 
}
运行结果:
A begin methodA
B begin methodB
BmethodB end
AmethodA end

1.5脏读

脏读是指在读取实例变量的时候,这个值已经被其他线程更改过了,也就是不同线程争抢实例变量的结果

public class PublicVar {
    public String username = "A";
    public String password = "AA";
    
    synchronized public void setValue(String username,String password){
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setvalue method thread name"+Thread.currentThread().getName());
            System.out.println(username +"  "+password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
     public  void getValue(){
        System.out.println("getvalue method thread name"+Thread.currentThread().getName());
        System.out.println(username +"  "+password);
    } 
}

public class ThreadA extends Thread{ 
    private PublicVar pv;
    public ThreadA(PublicVar pv){
        super();
        this.pv = pv;
    }
    @Override
    public void run() {
        super.run();
        pv.setValue("B", "BB");
    }
}

public class Run { 
    public static void main(String[] args) { 
         try {
            PublicVar pv = new PublicVar();
             ThreadA thread = new ThreadA(pv);
             thread.start();
             Thread.sleep(200);
             pv.getValue();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } 
}
运行结果:
getvalue method thread namemain
B  AA
setvalue method thread nameThread-0
B  BB
public class PublicVar {
    public String username = "A";
    public String password = "AA";
    
    synchronized public void setValue(String username,String password){
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setvalue method thread name"+Thread.currentThread().getName());
            System.out.println(username +"  "+password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    synchronized public  void getValue(){
        System.out.println("getvalue method thread name"+Thread.currentThread().getName());
        System.out.println(username +"  "+password);
    } 
}

运行结果:
setvalue method thread nameThread-0
B  BB
getvalue method thread namemain
B  BB

1.6synchronized锁重入

当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次获取到该对象的锁的
也就是synchronized内部调用本类的其他synchronized方法时,永远是可以得到锁的

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");
    }
}


public class MyThread extends Thread { 
    @Override
    public void run() { 
        Service service = new Service();
        service.service1();
    } 
}

public class Run { 
    public static void main(String[] args) { 
        MyThread thread = new MyThread();
        thread.start();
    } 
}

1.7出现异常,锁自动释放

线程a出现异常,释放锁,线程b执行

public class Service { 
    synchronized public void testMethod(){
        if(Thread.currentThread().getName().equals("A")){
            System.out.println("before exception");
            Integer.parseInt("a");
            System.out.println("Thread A run");
        }else{
            System.out.println("Thread B run ");
        }
    } 
}

public class ThreadA extends Thread{ 
    private Service service;
    public ThreadA(Service service){
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
    }
}

public class ThreadB extends Thread { 
    private Service service;
    public ThreadB(Service service){
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
    }
}

public class Run { 
    public static void main(String[] args) { 
         Service service = new Service();
         ThreadA threada = new ThreadA(service);
         threada.setName("A");
         threada.start(); 
     
         ThreadB threadb = new ThreadB(service);
         threadb.setName("B");
         threadb.start(); 
    } 
}


运行结果
before exception
Thread B run 
Exception in thread "A" java.lang.NumberFormatException: For input string: "a"
    at java.lang.NumberFormatException.forInputString(Unknown Source)
    at java.lang.Integer.parseInt(Unknown Source)
    at java.lang.Integer.parseInt(Unknown Source)
    at t1.Service.testMethod(Service.java:7)
    at t1.ThreadA.run(ThreadA.java:12)

1.8 同步不具有继承性

public class Main {
    synchronized public void testMethod(){
        try {
            System.out.println("main begin");
            Thread.sleep(3000);
            System.out.println("main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Sub extends Main {
    @Override
    public  void testMethod() {
        try {
            System.out.println("sub begin");
            Thread.sleep(3000);
            System.out.println("sub end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private Sub sub;
    public ThreadA(Sub sub){
        this.sub = sub;
    }
    @Override
    public void run() {
        super.run();
        sub.testMethod();
    }
}


public class ThreadB extends Thread {
    private Sub sub;
    public ThreadB(Sub sub){
        this.sub = sub;
    }
    @Override
    public void run() {
        super.run();
        sub.testMethod();
    }
}

public class Run {
 
    public static void main(String[] args) {
        Sub sub = new Sub();
        ThreadA threada = new ThreadA(sub);
        ThreadB threadb = new ThreadB(sub);
        threada.start();
        threadb.start(); 
    } 
}
运行结果:虽然是继承的,但是如果没有加synchronized关键字的话,并不能同步,如果要同步还是需要synchronized关键字
sub begin
sub begin
sub end
sub end

2.synchronized同步语句块

synchronized方法是有弊端的,如果该方法需要执行时间非常长的话,另一个线程就要一直等待

public class CommonUtils {

    public static long beginTime1;
    public static long endTime1;

    public static long beginTime2;
    public static long endTime2;
}

public class Task {

    private String getData1;
    private String getData2;

    public synchronized void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            getData1 = "长时间处理任务后从远程返回的值1 threadName="
                    + Thread.currentThread().getName();
            getData2 = "长时间处理任务后从远程返回的值2 threadName="
                    + Thread.currentThread().getName();
            System.out.println(getData1);
            System.out.println(getData2);
            System.out.println("end task");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MyThread1 extends Thread { 
    private Task task; 
    public MyThread1(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime1 = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime1 = System.currentTimeMillis();
    } 
}

public class MyThread2 extends Thread { 
    private Task task; 
    public MyThread2(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime2 = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime2 = System.currentTimeMillis();
    } 
}


public class Run {

    public static void main(String[] args) {
        Task task = new Task();

        MyThread1 thread1 = new MyThread1(task);
        thread1.start();

        MyThread2 thread2 = new MyThread2(task);
        thread2.start();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long beginTime = CommonUtils.beginTime1;
        if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {
            beginTime = CommonUtils.beginTime2;
        }

        long endTime = CommonUtils.endTime1;
        if (CommonUtils.endTime2 > CommonUtils.endTime1) {
            endTime = CommonUtils.endTime2;
        }

        System.out.println("耗时:" + ((endTime - beginTime) / 1000));
    }
}
运行结果:
begin task
长时间处理任务后从远程返回的值1 threadName=Thread-0
长时间处理任务后从远程返回的值2 threadName=Thread-0
end task
begin task
长时间处理任务后从远程返回的值1 threadName=Thread-1
长时间处理任务后从远程返回的值2 threadName=Thread-1
end task
耗时:6

2.2同步代码块

public class Task {

    private String getData1;
    private String getData2;

    public void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);

            String privateGetData1 = "长时间处理任务后从远程返回的值1 threadName="
                    + Thread.currentThread().getName();
            String privateGetData2 = "长时间处理任务后从远程返回的值2 threadName="
                    + Thread.currentThread().getName();

            synchronized (this) {
                getData1 = privateGetData1;
                getData2 = privateGetData2;
            }
            
            System.out.println(getData1);
            System.out.println(getData2);
            System.out.println("end task");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

2.5一半同步一半不同步

public class Task { 
    public void doLongTimeTask() {
        for (int i = 0; i < 10 ; i++) {
            System.out.println("nosynchronized threadName="
                    + Thread.currentThread().getName() + " i=" + (i + 1));
        }
        System.out.println("");
        synchronized (this) {
            for (int i = 0; i < 10 ; i++) {
                System.out.println("synchronized threadName="
                        + Thread.currentThread().getName() + " i=" + (i + 1));
            }
        } 
    }
}

public class MyThread1 extends Thread { 
    private Task task; 
    public MyThread1(Task task) {
        super();
        this.task = task;
    } 
    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    } 
}

public class MyThread2 extends Thread { 
    private Task task; 
    public MyThread2(Task task) {
        super();
        this.task = task;
    } 
    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    } 
}

public class Run { 
    public static void main(String[] args) {
        Task task = new Task(); 
        MyThread1 thread1 = new MyThread1(task);
        thread1.start(); 
        MyThread2 thread2 = new MyThread2(task);
        thread2.start();
    }
}



运行结果:非同步的交叉打印,同步的排队打印
nosynchronized threadName=Thread-0 i=1
nosynchronized threadName=Thread-0 i=2
nosynchronized threadName=Thread-1 i=1
nosynchronized threadName=Thread-1 i=2
nosynchronized threadName=Thread-1 i=3
nosynchronized threadName=Thread-1 i=4
nosynchronized threadName=Thread-1 i=5
nosynchronized threadName=Thread-1 i=6
nosynchronized threadName=Thread-1 i=7
nosynchronized threadName=Thread-1 i=8
nosynchronized threadName=Thread-1 i=9
nosynchronized threadName=Thread-1 i=10

synchronized threadName=Thread-1 i=1
synchronized threadName=Thread-1 i=2
synchronized threadName=Thread-1 i=3
synchronized threadName=Thread-1 i=4
synchronized threadName=Thread-1 i=5
synchronized threadName=Thread-1 i=6
synchronized threadName=Thread-1 i=7
synchronized threadName=Thread-1 i=8
synchronized threadName=Thread-1 i=9
synchronized threadName=Thread-1 i=10
nosynchronized threadName=Thread-0 i=3
nosynchronized threadName=Thread-0 i=4
nosynchronized threadName=Thread-0 i=5
nosynchronized threadName=Thread-0 i=6
nosynchronized threadName=Thread-0 i=7
nosynchronized threadName=Thread-0 i=8
nosynchronized threadName=Thread-0 i=9
nosynchronized threadName=Thread-0 i=10

synchronized threadName=Thread-0 i=1
synchronized threadName=Thread-0 i=2
synchronized threadName=Thread-0 i=3
synchronized threadName=Thread-0 i=4
synchronized threadName=Thread-0 i=5
synchronized threadName=Thread-0 i=6
synchronized threadName=Thread-0 i=7
synchronized threadName=Thread-0 i=8
synchronized threadName=Thread-0 i=9
synchronized threadName=Thread-0 i=10

2.3synchronized代码块间的同步性

public class ObjectService {

    public void serviceMethodA() {
        try {
            synchronized (this) {
                System.out.println("A begin time=" + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("A end    end=" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void serviceMethodB() {
        synchronized (this) {
            System.out.println("B begin time=" + System.currentTimeMillis());
            System.out.println("B end    end=" + System.currentTimeMillis());
        }
    }
}

public class ThreadA extends Thread {

    private ObjectService service;

    public ThreadA(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethodA();
    } 
}

public class ThreadB extends Thread {
    private ObjectService service;

    public ThreadB(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethodB();
    }
}

public class Run {

    public static void main(String[] args) {
        ObjectService service = new ObjectService();

        ThreadA a = new ThreadA(service);
        a.setName("a");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("b");
        b.start();
    }

}
运行结果:两个同步代码块按顺序执行
同步的锁都是this
A begin time=1472655529707
A end    end=1472655531707
B begin time=1472655531707
B end    end=1472655531707

2.6验证synchronized(this)代码块是锁定当前对象的

public class Task {

     synchronized public void otherMethod() {
        System.out.println("------------------------run--otherMethod");
    }

    public void doLongTimeTask() {
        try {
            synchronized (this) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("synchronized threadName="
                            + Thread.currentThread().getName() + " i=" + (i + 1));
                    Thread.sleep(100);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } 
    }
}

public class MyThread1 extends Thread {

    private Task task;

    public MyThread1(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    } 
}

public class MyThread2 extends Thread {

    private Task task;

    public MyThread2(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.otherMethod();
    } 
}

public class Run { 
    public static void main(String[] args) throws InterruptedException {
        Task task = new Task(); 
        MyThread1 thread1 = new MyThread1(task);
        thread1.start(); 
        Thread.sleep(100); 
        MyThread2 thread2 = new MyThread2(task);
        thread2.start();
    }
}
运行结果:othermethod方法是否synchronized运行结果不一样,证明用的是同一个锁

2.7将任意对象作为对象监视器

使用synchronized(非this)同步的话,必须用同一个对象才能同步

public class Service {

    private String usernameParam;
    private String passwordParam;
//  String anyString = new String();位置1
    public void setUsernamePassword(String username, String password) {
        try {
            String anyString = new String();//位置2
            synchronized (anyString) {
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "进入同步块");
                usernameParam = username;
                Thread.sleep(3000);
                passwordParam = password;
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "离开同步块");
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } 
}

public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        service.setUsernamePassword("a", "aa");
    }
}

public class ThreadB extends Thread {

    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        service.setUsernamePassword("b", "bb");
    }
}

public class Run {

    public static void main(String[] args) {
        Service service = new Service();

        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start(); 
    } 
}
运行结果:位置1使用的监视器是同一个,同步调用
位置2使用的话两个线程用的监视器不是同一个,异步调用
` ``


```java
//方法a使用的监视器对象是str对象,方法b使用的是this对象
public class Service { 
    private String anyString = new String();  
    public void a() {
        try {
            synchronized (anyString) {
                System.out.println("a begin");
                Thread.sleep(3000);
                System.out.println("a   end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void b() {
        System.out.println("b begin");
        System.out.println("b   end");
    } 
}

public class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.a(); 
    } 
}

public class ThreadB extends Thread {

    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.b(); 
    } 
}

public class Run {
    //使用的是不同的锁,不会同步
    public static void main(String[] args) {
        Service service = new Service();

        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();  
    } 
}

2.8三个结论

2.8.1多个线程同时执行synchronized(x){}同步代码块时呈同步效果

public class MyObject { }

public class MyService {
    public void testMethod1(MyObject obj){
        synchronized (obj) {
            try {
                System.out.println(Thread.currentThread().getName()+"  getLock");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"  releaseLock");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadA extends Thread {
    private MyService ser;
    private MyObject obj;
    public ThreadA(MyService ser,MyObject obj){
        super();
        this.ser = ser;
        this.obj = obj;
    }
    @Override
    public void run() {
        super.run();
        ser.testMethod1(obj);
    }
}

public class ThreadB extends Thread {

    private MyService ser;
    private MyObject obj;
    public ThreadB(MyService ser,MyObject obj){
        super();
        this.ser = ser;
        this.obj = obj;
    }
    @Override
    public void run() {
        super.run();
        ser.testMethod1(obj);
    } 
}


public class Run { 
    public static void main(String[] args)     { 
        MyObject obj = new MyObject();
        MyService ser = new MyService();
        ThreadA ta = new ThreadA(ser,obj);
        ta.setName("a");
        ThreadB tb = new ThreadB(ser,obj);
        tb.setName("b");
        ta.start();
        tb.start();
    } 
}
运行结果是同步的,因为使用的是同一个MyObject对象作锁

2.8.2当其他线程执行x对象的sychronized同步方法呈同步效果

public class MyObject {
    synchronized public void printStr(){
        System.out.println(Thread.currentThread().getName() + "printStr getLock");
        System.out.println("--------------");
        System.out.println(Thread.currentThread().getName()+"printStr releaseLock");
    }
}

public class MyService {
    public void testMethod1(MyObject obj){
        synchronized (obj) {
            try {
                System.out.println(Thread.currentThread().getName()+" testMethod1  getLock");
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName()+" testMethod1 releaseLock");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadA extends Thread {
    private MyService ser;
    private MyObject obj;
    public ThreadA(MyService ser,MyObject obj){
        super();
        this.ser = ser;
        this.obj = obj;
    }
    @Override
    public void run() {
        super.run();
        ser.testMethod1(obj);
    }
}

public class ThreadB extends Thread {

    private MyService ser;
    private MyObject obj;
    public ThreadB(MyService ser,MyObject obj){
        super();
        this.ser = ser;
        this.obj = obj;
    }
    @Override
    public void run() {
        super.run();
        obj.printStr();
    } 
}

public class Run { 
    public static void main(String[] args)     { 
        MyObject obj = new MyObject();
        MyService ser = new MyService();
        ThreadA ta = new ThreadA(ser,obj);
        ta.setName("a");
        ThreadB tb = new ThreadB(ser,obj);
        tb.setName("b");
        ta.start();
        tb.start();
    } 
}
运行结果:testMethod1锁的是MyObject对象,printStr方法也是锁MyObject对象,而且是同一个对象的锁所以在第一个执行完成释放锁之后才会执行下一个
a testMethod1  getLock
a testMethod1 releaseLock
bprintStr getLock
--------------
bprintStr releaseLock

2.8.3当其他线程执行x对象的sychronized(this)代码块呈同步效果

上一个例子的代码换一下效果一样
public class MyObject {
     public void printStr(){
         synchronized(this){
                System.out.println(Thread.currentThread().getName() + "printStr getLock");
                System.out.println("--------------");
                System.out.println(Thread.currentThread().getName()+"printStr releaseLock");     
         } 
    }
}

2.9静态同步方法

synchronized加static方法是个Class文件上锁


public class Service {
    synchronized public static void printA(){
        try {
            System.out.println(Thread.currentThread().getName() +" 进入printA");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() +" 离开printA");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } 
    }
    
    synchronized public static void printB(){
        try {
            System.out.println(Thread.currentThread().getName() +" 进入printB");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() +" 离开printB");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }  
    }
}

public class ThreadA extends Thread {
       @Override
    public void run() {
        super.run();
        Service.printA();
       }
}

public class ThreadB extends Thread {
       @Override
    public void run() {
        super.run();
        Service.printB();
       }
}


public class Run {

    public static void main(String[] args) {
        ThreadA ta = new ThreadA();
        ThreadB tb = new ThreadB();
        ta.setName("a");
        tb.setName("b");
        ta.start();
        tb.start();
    } 
}

对象锁和Class锁是两个不同的概念,下面的代码会异步运行

public class Service {
    synchronized public static void printA(){
        try {
            System.out.println(Thread.currentThread().getName() +" 进入printA");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() +" 离开printA");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } 
    }
    
    synchronized public static void printB(){
        try {
            System.out.println(Thread.currentThread().getName() +" 进入printB");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() +" 离开printB");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }  
    }
    
    public synchronized void printC(){
        try {
            System.out.println(Thread.currentThread().getName() +" 进入printC");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() +" 离开printC");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }  
    }
}


public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printA();
       }
}

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printB();
       }
}

public class ThreadC extends Thread {
    private Service service;
    public ThreadC(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printC();
       }
}

public class Run { 
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA ta = new ThreadA(service);
        ThreadB tb = new ThreadB(service);
        ThreadC tc = new ThreadC(service);
        ta.setName("a");
        tb.setName("b");
        tc.setName("c");
        ta.start();
        tb.start();
        tc.start();
    } 
}
运行结果:
a 进入printA
c 进入printC
a 离开printA
b 进入printB
c 离开printC
b 离开printB 

2.10数据类型String的常量池特性

public class Service {
     public   void printA(String param){ 
         synchronized (param) {
            while(true){
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        }
     } 
}


public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printA("AA");
       }
}

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printA("BB");
       }
}

public class Run { 
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA ta = new ThreadA(service);
        ThreadB tb = new ThreadB(service);
         
        ta.setName("a");
        tb.setName("b");
         
        ta.start();
        tb.start(); 
    } 
}
运行结果:a b随机打印
public class Service {
     public   void printA(String param){ 
         synchronized (param) {
            while(true){
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        }
     } 
}


public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printA("AA");
       }
}

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.printA("AA");
       }
}

public class Run { 
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA ta = new ThreadA(service);
        ThreadB tb = new ThreadB(service);
         
        ta.setName("a");
        tb.setName("b");
         
        ta.start();
        tb.start(); 
    } 
}
运行结果:两个线程使用了同一个锁对象"AA",a线程持有锁之后,一直运行,没有释放锁,b线程一直等待

2.12死锁问题

jps查看正在运行的java程序获取到id
jstack -l id查看运行情况

2.13内部类与静态内部类

public class OutObject {
    
    static class InnerObj1{
        //使用的锁是InnerObj2的实例对象
        public void method1(InnerObj2 obj2){
            String threadName = Thread.currentThread().getName(); 
            synchronized (obj2) {
                System.out.println(threadName+"进入InnerObj1 的 method1");
                for(int k=0;k<5;k++){
                    System.out.println("k="+k);
                    try {
                        Thread.sleep(600);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(threadName+"离开InnerObj1 的 method1");
            }
        }
        //使用的锁是InnerObj1的实例对象
        public synchronized void method2(){ 
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+"进入InnerObj1 的 method2");
            for(int k=0;k<5;k++){
                System.out.println("k="+k);
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(threadName+"离开InnerObj1 的 method2"); 
        } 
    }
    
    static class InnerObj2{
        //使用的对象时InnerObj2的实例对象
        public synchronized void method1(){
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+"进入InnerObj2 的 method1");
            for(int k=0;k<5;k++){
                System.out.println("k="+k);
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(threadName+"离开InnerObj2 的 method1");
        }
    }
}


public class Run { 
    public static void main(String[] args) { 
        
        final InnerObj1  inObj1 = new InnerObj1();
        final InnerObj2  inObj2 = new InnerObj2();
        Thread t1 = new Thread(new Runnable(){ 
            @Override
            public void run() {
                inObj1.method1(inObj2);//1.传的是inObj2
            } 
        },"T1");
        
        Thread t2 = new Thread(new Runnable(){ 
            @Override
            public void run() {
                inObj1.method2( ); 
            } 
        },"T2");
        
        Thread t3 = new Thread(new Runnable(){ 
            @Override
            public void run() {
                inObj2.method1( ); //2.inObj2调用的方法
            } 
        },"T3");
        
        t1.start();
        t2.start();
        t3.start();
        
    } 
}
//T3  T1用的是同一个锁,innObj2对象所以是同步进行
//t2的所对象是另一个,所以跟其他两个线程异步

2.16锁对象的改变

public class Service {
    private String lock = "123";
     public   void testMethod( ){ 
         synchronized (lock) { 
                System.out.println(Thread.currentThread().getName()+" begin");
                lock  = "456";
                System.out.println(" lock changed ");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" end");
        }
     } 
}



public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
       }
}

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
       }
}

public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        Service service = new Service();
        ThreadA threada = new ThreadA(service);
        ThreadB threadb = new ThreadB(service);
        threada.setName("a");
        threadb.setName("b");
        threada.start();
//      Thread.sleep(500);
        threadb.start();
    } 
} 


运行结果Thread.sleep(500);注释与不注释
注释的话,lock,a线程先运行lock兑现发生改变,虽然a没有释放锁,但是b获取的锁 和a获取的锁不一样,b也可以执行
a begin
 lock changed 
b begin
 lock changed 
a end
b end

不注释的话,a b同时执行,争抢"123"这个锁,只有当先执行完成的释放锁之后,后执行的才开始
a begin
 lock changed 
a end
b begin
 lock changed 
b end

只要对象不变,即使是对象的属性发生变化,仍然是同步效果

public class UserInfo {
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    } 
}



public class Service { 
     public   void testMethod( UserInfo userinfo){ 
         synchronized (userinfo) { 
                System.out.println(Thread.currentThread().getName()+" begin");
                userinfo.setUsername("abcabcabc");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" end");
        }
     } 
}


public class ThreadA extends Thread {
    private Service service;
    private UserInfo info;
    public ThreadA(Service service,UserInfo info){
        this.service = service;
        this.info = info;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod(info);
       }
}

public class ThreadB extends Thread {
    private Service service;
    private UserInfo info;
    public ThreadB(Service service,UserInfo info){
        this.service = service;
        this.info = info;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod(info);
       }
}

public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        Service service = new Service();
        UserInfo info = new UserInfo();
        ThreadA threada = new ThreadA(service,info);
        ThreadB threadb = new ThreadB(service,info);
        threada.setName("a");
        threadb.setName("b");
        threada.start();
        Thread.sleep(500);
        threadb.start();
    } 
} 

运行结果:
虽然a先运行,所对象的属性发生了改变,但是对象并没有改变,还是同步

3.volatile关键字:使得变量在多个线程间可见,强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值

volatile解决的是变量在多个线程之间的可见性
synchronized解决的是多个线程访问资源的同步性

synchronized与volatile之间的对比
1.volatile是线程同步的轻量实现,性能比synchronized好,volatile只能修饰变量,synchronized可以修饰方法以及代码块
2.多线程访问volatile不会发生阻塞
3.volatile能保证数据的可见性,但是不保证原子性,synchronized保证原子性的同时保证可见性,因为synchronized将私有内存和公共内存的数据做了同步

3.1死循环问题

public class PringString {
    private boolean isContinuePrint = true;

    public void printStrMethod(){
        try {
            while(isContinuePrint == true){
                System.out.println("PRINT");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } 

    public boolean isContinuePrint() {
        return isContinuePrint;
    }

    public void setContinuePrint(boolean isContinuePrint) {
        this.isContinuePrint = isContinuePrint;
    } 
}

public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        PringString ps  = new PringString();
        ps.printStrMethod();
        ps.setContinuePrint(false);//停不下来是因为主线程一直在循环 
    } 
} 

3.2server模式下的情形

public class RunThread extends Thread {
    private boolean isRunning = true;
    @Override
    public void run() {
        super.run();
        System.out.println("run begin");
        while(isRunning){
            
        }
        System.out.println("run end");
    }
    
    public boolean isRunning() {
        return isRunning;
    }
    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    } 
}

public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        RunThread rt = new RunThread();
        rt.start();
        Thread.sleep(3000);
        System.out.println(" 赋值为false");
        rt.setRunning(false);
    } 
} 

正常运行:
run begin
 赋值为false
run end

server模式下
run begin
 赋值为false

在启动线程时,private boolean isRunning = true;,存在与公共堆栈以及线程的私有堆栈中,在jvm设置server模式的时候,为了线程运行的效率,线程一直在私有堆栈中获取isRunning的值,而代码setRunning(false)虽然执行了,但是更新的是公共堆栈的值,所以一直是死循环
问题就是公共堆栈的值与线程私有堆栈的值不同步造成的,解决问题就是使用volatile关键字,它的主要作用就是当线程访问volatile修饰的变量的时候,强制从公共堆栈中进行取值。
2-75 2-77图

3.3volatile非原子的特性

2-80

3.4使用原子类进行操作

public class MyThread implements Runnable {
    private AtomicInteger count = new AtomicInteger();
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println(count.incrementAndGet());
        }
    } 
}


public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        MyThread mt = new MyThread();
        Thread  t1 = new Thread(mt);
        t1.start();
        Thread  t2 = new Thread(mt);
        t2.start();
        Thread  t3 = new Thread(mt);
        t3.start();
        Thread  t4 = new Thread(mt);
        t4.start();
        Thread  t5 = new Thread(mt);
        t5.start();
    } 
} 

运行结果:同步的

3.5原子类的不安全性

原子操作时安全的,但是两个原子操作却不是同步的,可能会出现脏读


public class Service { 
    public static AtomicLong al = new AtomicLong();
    synchronized public   void addNum( ){ 
          System.out.println(Thread.currentThread().getName() + " +100= "+al.addAndGet(100));
          al.addAndGet(1);
     } 
}



public class MyThread  extends Thread{
    private Service service;
    public MyThread(Service service){
        this.service = service;
    }
    
    @Override
    public void run() {
        super.run();
        service.addNum();
    } 
}



public class Run { 
    public static void main(String[] args) throws InterruptedException { 
        Service service = new Service();
        MyThread mt1 = new MyThread(service);
        mt1.start();
        MyThread mt2 = new MyThread(service);
        mt2.start();
        MyThread mt3 = new MyThread(service);
        mt3.start();
        MyThread mt4 = new MyThread(service);
        mt4.start();
        Thread.sleep(1000);
        System.out.println(service.al.get());
    } 
} 
addNum加不加synchronized声明效果不一样
addAndGet虽然是同步的,但是方法与方法之间的调用却不是同步的
不加synchronized:
Thread-1 +100= 100
Thread-3 +100= 400
Thread-2 +100= 300
Thread-0 +100= 200
404
加synchronized:
Thread-0 +100= 100
Thread-2 +100= 201
Thread-1 +100= 302
Thread-3 +100= 403
404

3.6synchronized代码块有volatile的功能

synchronized可以使多个线程访问同一个资源具有同步性,而且它还具有将线程中内存私有变量与公共内存中变量同步的功能。
synchronized可以保证在同一个时刻,只有一个线程可以执行一个方法或者代码块,它包含两个特征:互斥性和可见性

上一篇 下一篇

猜你喜欢

热点阅读