同步、异步、类锁、对象锁

同步

一个任务的执行,需要依赖另一个任务的完成。
标准的程序执行流程就是同步的,只有上一个函数执行完成,下一个函数才可以开始执行,这就是依赖关系

异步

一个任务的执行,不需要依赖另一个任务的完成。
并发的概念,只需要告诉另一个任务需要干什么,它会直接去执行

对象锁:只有竞争同一个对象的时候

  • synchronized修饰非静态方法代码块synchronized (this)synchronized (非this对象)线程要执行对应的同步代码,需要获取对象锁

锁住方法

先展示一个没有锁的例子,他的输出结果根据代码可以知道,因为我们实例化了一个对象,然后先开启了一个线程传了一个参数a过去,执行到System.out.println("a arrived");他就开始睡眠2s,于此同时又开启了另一个线程,同样调用的是同一对象的同一个方法,传递了b过去,所以出现下面这个结果

public class main {

    public static void main(String[] args) throws IOException {
        person p = new person();
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.addstr("a");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.addstr("b");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        }
    }


class person{
    private int number = 0;
    public void addstr(String string) throws InterruptedException {
        if (string.equals("a")) {
            number = 100;
            System.out.println("a arrived");
            Thread.sleep(2000);
        }else {
            number = 200;
            System.out.println("b arrived");
            }
        System.out.println(string + " number=" + String.valueOf(number));
        }
    }

输出结果:

a arrived
b arrived
b number=200
a number=200

稍作更改给addstr方法加上锁,这时候多线程竞争同一个对象的带锁的方法时,就要按顺序来,要等上一个调用释放锁

class person{
    private int number = 0;
    synchronized public void addstr(String string) throws InterruptedException {
        if (string.equals("a")) {
            number = 100;
            System.out.println("a arrived");
            Thread.sleep(2000);
        }else {
            number = 200;
            System.out.println("b arrived");
            }
        System.out.println(string + " number=" + String.valueOf(number));
        }
    }

还有一种情况就是,锁住一个类的两个方法,两个线程同时调用锁住的不同对象,这种情况,经过测试,输出结果显示,执行第二个被锁住的方法也需要等待,也就是一个类内,使用一个锁,锁住一个或多个方法

        person p = new person();
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.printA(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.printB(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        }
    }


class person{
    private int number = 0;
    synchronized public void printA(String string) throws InterruptedException {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    
    synchronized public void printB(String string) throws InterruptedException {
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
        }
}

输出结果:

A 进入线程!
A 离开线程!
B 进入线程!
B 离开线程!

synchronized (this)

再次对上面的person类稍作修改,我们锁住一个代码块,同样的当多个线程竞争同一个对象这个代码块时需要按照锁的规矩来,上一个释放,下一个才可以使用,输出结果同上

class person{
    private int number = 0;
    public void addstr(String string) throws InterruptedException {
        synchronized(this) {
        if (string.equals("a")) {
            number = 100;
            System.out.println("a arrived");
            Thread.sleep(2000);
        }else {
            number = 200;
            System.out.println("b arrived");
            }
        System.out.println(string + " number=" + String.valueOf(number));
        }
        }
    }

这里需要注意一下,因为多线程只有竞争这个代码块的内容才是使用锁的规矩,也就是这个代码块上面、下面都是不同步的,修改一下代码,这个这个同步代码块上面、下面加上两句输出System.out.println(string + "来了");、System.out.println(string + " number=" + String.valueOf(number));,这个时候输出结果有下面两种结果。首先解释一个b来了的输出,因为System.out.println(string + "来了");这一句没有锁,所以在第一个带有参数a睡眠的时候,第二个线程运行到这里输出完b来了,它就在这里等着,等第一个线程释放了锁,它才能继续执行。至于结果有两种,则是因为另外一个输出语句System.out.println(string + " number=" + String.valueOf(number));没有上锁,完全根据谁快、谁执行,或者并发执行都是可以的

class person{
    private int number = 0;
    public void addstr(String string) throws InterruptedException {
        System.out.println(string + "来了");
        synchronized(this) {
        if (string.equals("a")) {
            number = 100;
            System.out.println("a arrived");
            Thread.sleep(2000);
        }else {
            number = 200;
            System.out.println("b arrived");
            }
        }
        System.out.println(string + " number=" + String.valueOf(number));
        }
    }

输出结果:

a来了
a arrived
b来了
b arrived
a number=100
b number=200

或者

a来了
a arrived
b来了
b arrived
b number=200
a number=200

同样的,synchronized (this)也存在同一个类中,使用一个锁锁住一个或多个代码块,但是代码块上下是不同步的

class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    synchronized public void printA(String string) throws InterruptedException {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    
    public void printB(String string) throws InterruptedException {
        System.out.println("B ready!");
        synchronized (this) {
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
        }
    }

输出结果:

A 进入线程!
B ready!
A 离开线程!
B 进入线程!
B 离开线程!

synchronized (非this对象)

只要多线程访问的对象带有被锁住的对象,并且是同一个,就需要按照锁的规则来,就像下面的例子,虽然实例化了两个不同的对象,但是他们访问的都是同一个String对象,就有竞争关系。其实可以被锁住的对象就像一个标记,任何线程使用了这个被锁住的对象,就相当于被标记了,被标记的线程执行到synchronized声明的地方,就要按顺序来,不存在并发进行

public static void main(String[] args) throws IOException {
        String string = "son";
        person p = new person(string);
        person p1 = new person(string);
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.addstr("a");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p1.addstr("b");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        }
    }


class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    public void addstr(String string) throws InterruptedException {
        System.out.println(string + "来了");
        synchronized(son) {
        if (string.equals("a")) {
            number = 100;
            System.out.println("a arrived");
            Thread.sleep(2000);
        }else {
            number = 200;
            System.out.println("b arrived");
            }
        }
        System.out.println(string + " number=" + String.valueOf(number));
        }
    }

类锁

  • synchronized修饰静态方法synchronized(类.class),锁的是对应的类,需要获取类锁
    类锁其实也是使用对象锁,当加载一个class文件时,会创建一个java.lang.Class类的实例,锁一个类的时候就是锁住java.lang.Class类的实例

下面这个例子,是对象锁,使用不同person对象,即使用了不同的锁锁住person对象

        String string = "son";
        person p = new person(string);
        person p1 = new person(string);
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.printA(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p1.printB(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        }
    }


class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    synchronized public  void printA(String string) throws InterruptedException {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    
    synchronized public  void printB(String string) throws InterruptedException {
        System.out.println("B ready!");
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
    }
}

对象锁的输出结果:

A 进入线程!
B ready!
B 进入线程!
B 离开线程!
A 离开线程!

稍作修改加上static,让我们使用类锁,即使实例化了不同的对象,但是使用的同一个类,这时候的输出结果就不一样了,这里还用了同一个类,使用一个锁锁住一个或多个方法、代码块

class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    synchronized public static void printA(String string) throws InterruptedException {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    
    synchronized public static void printB(String string) throws InterruptedException {
        System.out.println("B ready!");
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
    }
}

输出结果:

A 进入线程!
A 离开线程!
B ready!
B 进入线程!
B 离开线程!

再次引入一个小心得点:类锁对象锁混用
同样使用上面的方法代码稍微修改了一下,可以从输出结果看出,使用对象锁的代码块,即使是同一个对象,也没有执行锁的机制,说明了类锁和对象锁混用的情况下,只有类锁有效,对象锁无效

this: 表示当前类实例化后的对象

        person p = new person(string);
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.printA(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    p.printB(string);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        }
    }

class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    synchronized public static void printA(String string) throws InterruptedException {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    
    public void printB(String string) throws InterruptedException {
        System.out.println("B ready!");
        synchronized (this) {
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
        }
    }
}

为了证明和锁的顺序没关系,调换顺序来证明

class person{
    public String son;
    public person(String son) {
        this.son = son;
    }
    private int number = 0;
    
    public void printA(String string) throws InterruptedException {
        System.out.println("A ready!");
        synchronized (this) {
        System.out.println("A 进入线程!");
        Thread.sleep(2000);
        System.out.println("A 离开线程!");
        }
    }
    
    synchronized public static void printB(String string) throws InterruptedException {
        System.out.println("B 进入线程!");
        System.out.println("B 离开线程!");
        }
}

输出结果:

A ready!
A 进入线程!
B 进入线程!
B 离开线程!
A 离开线程!

Java对象锁和类锁全面解析(多线程synchronized关键字)

发布了118 篇原创文章 · 获赞 14 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/github_38641765/article/details/86580439