今天做笔试的时候要写一个同步代码,忽然发现对synchronized和lock的使用不是很熟练,查阅了相关资料,练习了几个例子,对这两个的基本用法熟悉了,在这里记录下。
一、synchronized有四种用法,分别如下:
1、直接修饰实例方法,这时候锁住的是该方法所在的对象,想执行demo1.consumer()方法时必须先获得Demo类的实例对象demo1的锁。因此进行线程同步的时候必须确保要同步的线程调用的都是同一个对象的consumer()方法,因为每个对象都有一把锁,调用demo1.consumer()方法不需要获得demo2对象的锁,即调用demo1.consumer()和demo2.consumer()分别需要demo1和demo2的对象锁。
public class Demo{
private static int number = 10000;
synchronized void consumer() {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
2、直接修饰静态方法,这时候锁住的是该方法所在的类,即所有该类的所有对象拥有同一个锁,调用Demo.consumer()方法时需获取该类的锁,如果采用实例对象来调用,即调用demo1.consumer()和demo2.consumer()需要获取的是同一个对象锁,需要同步的两个线程分别调用demo1.consumer()和demo2.consumer()可实现同步。
public class Demo{
private static int number = 10000;
synchronized void consumer() {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3、直接修饰你想锁住的实例对象,如下面的demo跟第一种情况是等价的,synchronized (this)即锁住了Demo类的实例对象。
public class Demo{
static private int number = 10000;
void consumer() {
synchronized (this) {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4、直接修饰你想锁住的类(可以用于静态方法和实例方法),如下面举的例子跟第二个例子是等价的,即锁住了该类全部的实例对象。
public class Demo{
static private int number = 10000;
void consumer() {
synchronized (Demo.class) {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
二、lock的基本用法如下,其中lock是一个Lock类的实例对象(一般是Lock lock = new ReentrantLock()),如果lock是静态变量(用static修饰),那么使用下面用法锁住的就是lock所属的类,如果lock是实例变量(没用static修饰),那么使用下面用法锁住的就是lock所属的类的实例对象,举例如下:
lock.lock();
try {
} finally {
lock.unlock();
}
1、实例变量(不用static修饰),该例子效果与synchronized的第三种情况等价。
public class Demo{
static private int number = 10000;
private Lock lock = new ReentrantLock();
void consumer() {
this.lock.lock();
try {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}finally{
this.lock.unlock();
}
}
}
2、静态变量(用static修饰),该例子效果与synchronized的第四种情况等价。
public class Demo{
static private int number = 10000;
private static Lock lock = new ReentrantLock();
void consumer() {
lock.lock();
try {
if (number > 0) {
int num = number;
int x = num;
num = num - (int) (Math.random() * 400) - 100;
if (num < 0)
num = 0;
else
x = x - num;
number = num;
System.out.println("贷款了" + x + "元");
System.out.println("剩余" + num + "元");
if (num == 0)
System.out.println("贷款结束了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}finally{
lock.unlock();
}
}
}
三、本人测试过synchronized和lock无法同时使用,它们同时锁住同一个对象时无法实现同步的效果,我觉得应该是两种锁机制的实现方式不同而导致的。synchronized是采用JVM来进行同步,而Lock采用AQS同步框架和naive方法通过内核来实现同步。