在多线程中,锁是非常重要的一个东西。
在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,那么所有对象调用这个方法都是同步的,这个类锁对所有对象实例都起作用。