Java多线程学习笔记(一) synchronized同步方法

1.提出问题-实例变量非线程安全

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

1.1 何为非线程安全?

我的理解是多个线程对一个实例变量操作会出现值被更改,不同步的情况。

1.2 举例

1.2.1 有私有变量的类HasPrivateNum (供多线程们去调用)

public class HasPrivateNum {
    private int num = 0;
    public void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.sleep(2000);
            }else {
                num = 2;
                System.out.println("other man set over");
            }
            
            System.out.println(userName + " num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

这个类里面有一个私有变量 num,在addNum()函数里根据传的userName不同而赋值.

1.2.2 线程A

public class ThreadA  extends Thread{
    
    private HasPrivateNum num;

    public ThreadA(HasPrivateNum num){
        super();
        this.num = num;
    }
    
    @Override
    public void run(){
        super.run();
        num.addNum("wang.dong");
    }
}

1.2.3 线程B

public class ThreadB extends Thread{
    private HasPrivateNum num;

    public ThreadB(HasPrivateNum num){
        super();
        this.num = num;
    }

    @Override
    public void run(){
        super.run();
        num.addNum("other man");
    }
}

1.2.4 开启AB线程共同访问1个对象

public class Run {
    public static void main(String[] args) {
        HasPrivateNum numRef = new HasPrivateNum();

        //A
        ThreadA aThread = new ThreadA(numRef);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef);
        bThread.start();
    }
}

1.2.5 执行结果(多次执行有几率出现如下结果)

在这里插入图片描述
A和B这2个线程同时访问一个没有同步的方法,并给里面变量赋值,可能出现"非线程安全"问题

2. 解决问题-给方法上加synchronized

2.1 synchronized修饰范围

修饰对象 作用范围 作用对象
代码块 synchronized(this){}内 调用这个代码块的对象
方法(接口方法/构造方法不行) 整个方法 调用这个方法的实例对象
静态方法 整个方法 这个类的所有类对象(共用一把锁)
synchronized后面{} 这个类的所有对象(共用一把锁)

2.2 修改后的HasPrivateNum类

public class HasPrivateNum {
    private int num = 0;
    
    // add synchroized
    public synchronized void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.sleep(2000);
            }else {
                num = 2;
                System.out.println("other man set over");
            }
            System.out.println(userName + " num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

2.3 执行结果(多次执行一定是这样)

在这里插入图片描述
如图说明线程B是等待线程A执行完毕后并释放了锁,才能获取锁

3.思考问题-如果是多个对象的锁

在上面1.2.4里只定义了一个HasPrivateNum的对象,如果有个HasPrivateNum对象,AB线程范围不同的对象,会是同步还是异步呢?

3.1 修改Run类

public class RunTwo {
    public static void main(String[] args) {
        HasPrivateNum numRef_1 = new HasPrivateNum();
        HasPrivateNum numRef_2 = new HasPrivateNum();
        
        //A
        ThreadA aThread = new ThreadA(numRef_1);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef_2);
        bThread.start();
    }
}

A线程和B线程分别用不同的对象

3.2 运行结果

在这里插入图片描述
因为A线程睡了2秒,说是后打印的,这个2个线程是异步运行的,不相互影响
这个结果说明关键字synchronized的锁是对象锁,不是把一段代码或函数锁住

4.思考问题-非synchronized方法会锁着吗

一个对象有synchronized方法和非synchronized方法,如果线程A占用了synchronized方法,线程B是否可以访问这个对象的非synchronized方法?

4.1修改HasPrivateNum类

public class HasPrivateNum {
    private int num = 0;

    // add synchroized
    public synchronized void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.sleep(2000);
            }else {
                num = 2;
                System.out.println("other man set over");
            }
            System.out.println(userName + " num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    // not synchronized
    public void anotherMethod(){
        try{
            System.out.println(Thread.currentThread().getName() + " anotherMethod begin");

            Thread.sleep(2000);

            System.out.println(Thread.currentThread().getName() + " anotherMethod end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

4.2修改线程B调用非synchronized方法

public class ThreadB extends Thread{
    private HasPrivateNum num;

    public ThreadB(HasPrivateNum num){
        super();
        this.num = num;
    }

    @Override
    public void run(){
        super.run();
        //num.addNum("other man");
        num.anotherMethod();
    }
}

4.3 run方法

public class Run {
    public static void main(String[] args) {
        HasPrivateNum numRef = new HasPrivateNum();

        //A
        ThreadA aThread = new ThreadA(numRef);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef);
        bThread.start();
    }
}

4.4 运行结果

在这里插入图片描述
从结果看出线程A和线程B是异步执行的

  • 多个线程同时访问同一个object的synchronized(this)代码块时,一个时间只有一个线程,其他线程只能等待
  • 其他线程可以访问该object的非synchronized(this)代码块

这次暂时写这么多吧,加油!

猜你喜欢

转载自blog.csdn.net/winterking3/article/details/83615702