synchronized同步方法

Posted 霓裳梦竹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了synchronized同步方法相关的知识,希望对你有一定的参考价值。

“非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问的时候产生,产生的后果是脏读,也就是取到的数据是被更改过的。而“线程安全”就是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象。

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

“非线程安全”问题存在于“实例变量中”,如果是方法内的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”了。

package selfThread;

/**
 * Created by liping.sang on 2017/1/17 0017.
 */
public class HasSelfPrivateNum {
    public void addI(String username){
        try{
            int num=0;
            if(username.equals("a")){
                num=100;
                System.out.println("a set over");
                Thread.sleep(2000);
            }else {
                num=200;
                System.out.println("b set over");
            }
            System.out.println(username + " num = "+num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
package selfThread;

/**
 * Created by liping.sang on 2017/1/17 0017.
 */
public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;

    public ThreadA( HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    public void run(){
        super.run();
        numRef.addI("a");
    }
}
package selfThread;

/**
 * Created by Administrator on 2017/1/17 0017.
 */
public class ThreadB  extends  Thread{
    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }
    public void run(){
        super.run();
        numRef.addI("b");
    }
}
package selfThread;

/**
 * Created by liping.sang on 2017/1/17 0017.
 */
public class Run {
    public static  void main(String []  args){
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA athread  = new ThreadA(numRef);
        athread.start();
        ThreadB bthread  = new ThreadB(numRef);
        bthread.start();
    }
}
b set over
b num = 200
a set over
a num = 100

可见,方法中的变量不存在非线性安全的问题,永远都是非线程安全的,这是方法内部的变量是私有的特性造成的。

2、实例变量非线程安全

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

package service;

public class HasSelfPrivateNum {

    private int num = 0;

    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
package extthread;

import service.HasSelfPrivateNum;

public class ThreadA extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }

}
package extthread;

import service.HasSelfPrivateNum;

public class ThreadB extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}
package test;

import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

    public static void main(String[] args) {

        HasSelfPrivateNum numRef = new HasSelfPrivateNum();

        ThreadA athread = new ThreadA(numRef);
        athread.start();

        ThreadB bthread = new ThreadB(numRef);
        bthread.start();

    }

}
a set over !
b set over !
b num = 200
a num =200

此时是两个线程同时访问一个没有同步的方法,如果两个线程同时操作对象中的实例变量,则有可能出现“非线程安全”问题,

只要在public void  addI(String username)方法前加关键字synchronized即可 。

在两个线程访问同一个对象中的同步方法时一定是线程安全的。

3、多个对象多个锁

package service;

public class HasSelfPrivateNum {

    private int num = 0;

    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
package extthread;

import service.HasSelfPrivateNum;

public class ThreadA extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }

}
package extthread;

import service.HasSelfPrivateNum;

public class ThreadB extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}
package test;

import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

    public static void main(String[] args) {

        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();

        ThreadA athread = new ThreadA(numRef1);
        athread.start();

        ThreadB bthread = new ThreadB(numRef2);
        bthread.start();

    }

}
a set over !
b set over !
b num =200
a num =100

两个线程分别访问同一个类的两个不同实例的相同名称的同步方法 ,效果却是以异步的方式运行,上面,创建了两个业务对象,在系统中产生了两个锁,所以运行结果是异步的,结果是先打印b在打印a,

虽然在HasSelfPrivateNum中使用了synchronized关键字但却是异步的交叉的,这是因为关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当做锁,所以在示例中,哪个线程先止血带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象,但如果多个线程访问的是多个对象,则JVM会创建多个锁。上面示例中创建了两个类的对象,所以就会产生2个锁。

4、synchronized方法与锁对象

package extobject;

public class MyObject {

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

}
package extthread;

import extobject.MyObject;

public class ThreadA extends Thread {

    private MyObject object;

    public ThreadA(MyObject object) {
        super();
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }

}
package extthread;

import extobject.MyObject;

public class ThreadB extends Thread {

    private MyObject object;

    public ThreadB(MyObject object) {
        super();
        this.object = object;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
package test.run;

import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

    public static void main(String[] args) {
        MyObject object = new MyObject();
        ThreadA a = new ThreadA(object);
        a.setName("A");
        ThreadB b = new ThreadB(object);
        b.setName("B");

        a.start();
        b.start();
    }

}

此时结果为:

begin methodA  threadName = A
begin methodB threadName = B
end
end

如果修改MyObject

package extobject;

public class MyObject {

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

}

在ThreadB中修改run方法中修改为Object.methodB();

1)A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法

2)A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。

 

以上是关于synchronized同步方法的主要内容,如果未能解决你的问题,请参考以下文章

静态方法内的同步块将获取类级别锁或对象级别锁

JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法

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

Java的synchronized的同步代码块和同步方法的区别

关于Synchronized

关于Synchronized