什么是内置锁
- 首先它是由java提供的关键synchronized来实现的,其次synchronized实现的锁在执行完修饰的代码块后自动释放锁的,相对于显示锁必须要手动释放锁所以内置锁也被成为隐式锁。
- 内置锁是由JVM实现的,jvm能基于synchronized锁做一些优化,比如增加锁的粒度(锁粗化)、锁消除。
内置锁的特性
- 相对显示锁来说,内置锁还是过重,因为内置锁是一个互斥锁,不仅读写互斥并且读读也互斥,最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。
- synchronized关键字不能继承,父类方法中加了synchronized,在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,需要重新加锁。
- 内置锁是非公平锁,线程在竞争synchronized锁时并不遵循先到等待队列就先获得锁,如果一个线程来请求锁,刚好该锁被释放了,那么这个线程可能会跳过在等待队列中的其它线程直接获得该锁。
- 内置锁是可重入锁,如果已经获取了一个锁对象,在还没释放时又要执行该锁对象的另一个代码块或方法,则不需要再次给这个对象加锁就可以直接执行。
内置锁(Synchronized)的使用
1.synchronized修饰代码块
- 使用方式如下,将需要同步的代码填入{}内,而关键字synchronized后的()内填入作为锁的对象。任何对象都可以作为锁,除了基本数据类型。
synchronized (Object) {
// todo
}
- 范例:
public class RunnableDemo implements Runnable {
static int i = 1;
@Override
public void run() {
while (i < 100) {
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName()+"计数:" + i);
i++;
}
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
RunnableDemo runnableSimple=new RunnableDemo();
Thread t = new Thread(runnableSimple, "thread-1");
Thread t2 = new Thread(runnableSimple, "thread-2");
t2.start();
t.start();
}
}
2.synchronized修饰实例方法
- 使用方式如下,这时锁的对象就是调用该方法的对象
public synchronized void method()
{
// todo
}
-
修饰实例方法一样能通过修饰代码块的方式实现,但是这种写法使代码更简洁了。
-
但是修饰方法会比修饰代码块的执行效率低,因为方法的创建与销毁都是需要耗时的,修饰在方法上,方法的创建与销毁时也都是占用锁的,导致了不必要的浪费。
-
范例:
public class RunnableDemo implements Runnable {
static int i = 1;
@Override
public void run() {
while (i < 100) {
this.excuCount();
}
}
public synchronized void excuCount(){
while (i < 100) {
try {
System.out.println(Thread.currentThread().getName()+"计时:" + i);
i++;
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
RunnableDemo runnableSimple=new RunnableDemo();
Thread t = new Thread(runnableSimple, "thread-1");
Thread t2 = new Thread(runnableSimple, "thread-2");
t2.start();
t.start();
}
}
synchronized修饰静态方法
- 使用方式如下,这时锁的对象就是该静态方法所属的类
public static synchronized void method()
{
// todo
}
-
类也是对象,本质上来说和修饰普通方法没有区别。但是修饰静态方法或修饰代码块时用类作锁的好处是能保证这个代码块被唯一的锁上锁了。
-
如下面例子中两个线程执行了了两个RunnableDemo实例,但是两个线程在执行excuCount方法时还是同步的,因为他们的锁就是唯一的RunnableDemo.class。把上面两个范例中的main方法同样改为两个不同RunnableDemo实例的线程,他们就不会同步了,因为锁的对象不是同一个RunnableDemo实例对象了。
-
范例:
public class RunnableDemo implements Runnable {
static int i = 1;
@Override
public void run() {
while (i < 100) {
this.excuCount();
}
}
public static synchronized void excuCount(){
while (i < 100) {
try {
System.out.println(Thread.currentThread().getName()+"计时:" + i);
i++;
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new RunnableDemo(), "thread-1");
Thread t2 = new Thread(new RunnableDemo(), "thread-2");
t2.start();
t.start();
}
}