为了避免出现上一张的结果,那么Java就是用了synchronized关键字。
再使用了synchronized关键字之后 一个类的实例如果想调用锁住的代码,那么这个实例必须要能够拿到对应的锁才行。而一个锁只能同时被一个实例拿到。
那么判断的根本依据就在于:实例需要拿到的是不是同一个锁! 如果是同一个锁,那么对应代码就不能同时执行,如果不是同一个锁,那么对应的代码就可以同时执行。
他有四种情况:
第一种--->代码块对象锁 :
public class Test implements Runnable {
static Test instance = new Test();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
L.p("Test main 函数执行完毕");
}
@Override
public void run() {
synchronized (lock){
L.p("我叫做" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
L.p(Thread.currentThread().getName() + " 执行结束");
}
}
}
程序输出结果应该是:
我叫做Thread-0 // 1 先打印,三秒钟后 2 3几乎同时打印 ,三秒钟后,4 5 几乎同时打印
Thread-0 执行结束
我叫做Thread-1
Thread-1 执行结束
Test main 函数执行完毕
对比来看 如果将代码做如下修改:
public class Test implements Runnable {
static Test instance1 = new Test();
static Test instance2 = new Test();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
L.p("Test main 函数执行完毕");
}
@Override
public void run() {
synchronized (lock){
L.p("我叫做" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
L.p(Thread.currentThread().getName() + " 执行结束");
}
}
}
我们这次不是同一个实例了,变成两个不同的实例,那么程序的输出应该如下:
我叫做Thread-1 // 1 2几乎同时打印,三秒后 3 4 5几乎同时打印
我叫做Thread-0
Thread-1 执行结束
Thread-0 执行结束
Test main 函数执行完毕
为什么会出现这样的情况?根据我们判断的依据 instance1和instance2需要的是两个不同的锁,因此可以同时执行 互不影响。
在上次改变的基础上 我们再做改变 将 “Object lock = new Object();” 改成 “static Object lock = new Object();”
程序会做如下输出:
我叫做Thread-0 // 1 先打印,三秒钟后 2 3几乎同时打印 ,三秒钟后,4 5 几乎同时打印
Thread-0 执行结束
我叫做Thread-1
Thread-1 执行结束
Test main 函数执行完毕
原因还是要从判断标准出发:instance1 和 instance2 需要拿到的应该是同一个锁,这是因为 static修饰的成员被所有的对象共享。也就是说 lock对象被 instance1 和 instance2共享,instances1 和 instance2需要的都是这个lock锁,因此当instance1执行时 instance2就无法执行,必须要等到instance1执行完毕 instance2才能拿到它需要的锁,然后执行相应代码。
第二种--->普通方法锁:
public class Test implements Runnable {
static Test instance = new Test();
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
L.p("Test main 函数执行完毕");
}
@Override
public void run() {
method();
}
public synchronized void method() {
L.p("我叫做" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
L.p(Thread.currentThread().getName() + " 执行结束");
}
}
输出如下(原因同理不再赘述):
我叫做Thread-0 // 1 先打印,三秒钟后 2 3几乎同时打印 ,三秒钟后,4 5 几乎同时打印
Thread-0 执行结束
我叫做Thread-1
Thread-1 执行结束
Test main 函数执行完毕
做修改:
public class Test implements Runnable {
static Test instance1 = new Test();
static Test instance2 = new Test();
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
L.p("Test main 函数执行完毕");
}
@Override
public void run() {
method();
}
public synchronized void method() {
L.p("我叫做" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
L.p(Thread.currentThread().getName() + " 执行结束");
}
}
输出如下(原因同理不再赘述):
我叫做Thread-1 // 1 2几乎同时打印,三秒后 3 4 5几乎同时打印
我叫做Thread-0
Thread-1 执行结束
Thread-0 执行结束
Test main 函数执行完毕
在上次修改基础上再做修改 将“public synchronized void method()”改为“public static synchronized void method()”(这其实可以划分到第三种类锁中...)
输出如下(原因同理不再赘述):
我叫做Thread-0 // 1 先打印,三秒钟后 2 3几乎同时打印 ,三秒钟后,4 5 几乎同时打印
Thread-0 执行结束
我叫做Thread-1
Thread-1 执行结束
Test main 函数执行完毕
第三种-->
public class Test implements Runnable {
static Test instance1 = new Test();
static Test instance2 = new Test();
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
L.p("Test main 函数执行完毕");
}
@Override
public void run() {
synchronized (Test.class) {
L.p("我叫做" + Thread.currentThread().getName());
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
L.p(Thread.currentThread().getName() + " 执行结束");
}
}
}
输出如下(原因同理不再赘述):
我叫做Thread-0 // 1 先打印,三秒钟后 2 3几乎同时打印 ,三秒钟后,4 5 几乎同时打印
Thread-0 执行结束
我叫做Thread-1
Thread-1 执行结束
Test main 函数执行完毕