关于多线程中锁的理解

在多线程中,锁是非常重要的一个东西。

在Java语言中,有对象和类之分,因此多线程的锁也可分为对象锁和类锁。

对象锁,顾名思义就是使用一个对象作为锁在多线程中使用;而类锁,这个主要是在类级别的方法或者代码块上加的锁,比如说static方法或static代码块。

因为是多线程,所以锁是一个共享资源,多线程争夺同一把锁。如果多线程使用了多把锁,那么程序代码执行与多线程就没多大关系了。

举个很简单的例子(有点儿不雅):

上卫生间蹲大号,每个茅坑只能一个人用。如果某个人进去了,他会把门锁上,那么这个锁就可以理解为多线程中的锁的概念。还有很多人想要用这个茅坑,但里面有人,因此其余人必须在外面等着。只有里面的人舒服了,开门出来了,这个时候会释放这个锁,其他人来争夺这个锁,抢到的进去蹲大号,抢不到的只能在外面等。有些人憋不住了,只能放弃(理解为线程超时,线程放弃)。

总结:

在使用synchronized关键字时,使用的锁必须是多线程共同使用的一把锁。

这里有几种写法:

1)多个线程对同一个对象的资源进行争夺。就是说某个对象的数据要在多个线程之间进行切换修改。如何实现呢?

public class ShareBody {

    public void print(int count) throws Exception {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "开始打印");
            for (int i = 1; i < count; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
            Thread.sleep(2000);
        }
    }
}

public class ThreadA implements Runnable {

    private ShareBody shareBody;

    public ThreadA(ShareBody shareBody) {
        this.shareBody = shareBody;
    }

    @Override
    public void run() {
        while (true) {
            try {
                this.shareBody.print((int)(Math.random() * 100));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        ShareBody shareBody = new ShareBody();
        ThreadA a = new ThreadA(shareBody);
        ThreadA b = new ThreadA(shareBody);
        ThreadA c = new ThreadA(shareBody);
        a.start();
        b.start();
        c.start();
    }
}

注意ShareBody类中synchroized关键字的写法,使用了this作为锁,也就是说使用Main类中shareBody这个对象作为了锁,三个线程使用的是同一个对象,那么争夺锁也是同一个。因此这三个线程会随机选择一个线程打印随机的次数(这里随机次数是因为在线程类中设置的是随机数)。

2)多个线程,每个线程使用的对象不同,但是使用了同一把锁。例如:

public class ObjectA {
    private Object _lock;
    public ObjectA(Object lock) {
        this._lock = lock;
    }

    public void methodA() throws Exception {
        synchronized(_lock) {
            //todo something
            Thread.sleep(2000);
        }
    }
}

public class ObjectB {
    private Object _lock;
    public ObjectB(Object lock) {
        this._lock = lock;
    }

    public void methodB() throws Exception {
        synchronized(_lock) {
            //todo something
            Thread.sleep(3000);
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Object lock = new Object();//创建锁对象
        ObjectA objA = new ObjectA(lock);//创建对象A
        ObjectB objB = new ObjectB(lock);//创建对象B
        new Thread(new Runnable(){
            @Override
            public void run(){
                objA.methodA();
            }
        }).start();
        new Thread(new Runnable(){
            @Override
            public void run(){
                objB.methodB();
            }
        }).start();
    }
}

在main方法中创建的两个线程操作了两个不同对象的不同方法,但是他们调用的同步方法使用了同一把对象锁。因此两个线程是阻塞同步的,一个线程执行,另外一个线程等待。

以上是对象锁。那么类锁呢?

类锁,其实就是在类级操作上加锁。常见的就是static关键字的使用。如果在类的某个方法上加关键字static,那么这个方法就是类方法。类级的操作是每个对象都共享的。所以,如果在static方法上加synchronized,那么所有对象调用这个方法都是同步的,这个类锁对所有对象实例都起作用。

猜你喜欢

转载自my.oschina.net/OHC1U9jZt/blog/1809304