一、可重入性
synchronized是具有可重入性的,在秋招阶段有两个公司问到这个问题,一个公司是直接问synchronized是否是可重入的,我当时的回答是“根据底层monitorenter和monitorexit原理,应该是可重入的”。另一家公司(可能是小米,难怪几个星期都没有收到二面通知)是给我出了一个题目:A()用synchronized修饰,B()用synchronized修饰,那么A调用B可以运行么,我当时答的不可以,没有想到重入性这点。还是不熟悉呀。
package com.louis.线程;
/**
*
* synchronized的重入
* */
public class ReenSync {
public synchronized void method1(){
System.out.println("method1..");
method2();
}
public synchronized void method2(){
System.out.println("method2..");
method3();
}
public synchronized void method3(){
System.out.println("method3..");
}
public static void main(String[] args) {
final ReenSync sd = new ReenSync();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
}
}
通过本例可说明synchronized具有可重入性。
二、父类与子类的关系
package com.louis.线程;
public class ReenSync2 {
static class Main {
public int i = 10;
public synchronized void operationSup(){
try {
i--;
System.out.println("Main print i = " + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Sub extends Main {
public synchronized void operationSub(){
try {
while(i > 0) {
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(100);
this.operationSup();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
});
t1.start();
}
}
父子方法都是用synchronized,是线程安全的
三、出现异常,释放锁
package com.louis.线程;
public class SynException {
private int i = 0;
public synchronized void operation(){
while(true){
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if(i == 10){
Integer.parseInt("a");
// throw new RuntimeException();
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("log info i =" + i);
}
}
}
public static void main(String[] args) {
final SynException se = new SynException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}
四、使用synchronized代码块加锁
有对象锁、类锁、任何对象锁
package com.louis.线程;
/**
* 使用synchronized代码块加锁,比较灵活
* @author alienware
*
*/
public class ObjectLock {
public void method1(){
synchronized (this) { //对象锁
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){ //类锁
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){ //任何对象锁
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
});
t1.start();
t2.start();
t3.start();
}
}
五、对字符串常量加锁注意事项
package com.louis.线程;
/**
* synchronized代码块对字符串的锁,注意String常量池的缓存功能
* @author alienware
*
*/
public class StringLock {
public void method() {
//new String("字符串常量") //没问题
synchronized ("字符串常量") {//有问题
try {
while(true){
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2");
t1.start();
t2.start();
}
}
换成字符串对象即可。
六、更改锁对象
package com.louis.线程;
/**
* 锁对象的改变问题
*
*
*/
public class ChangeLock {
private String lock = "lock";
private void method(){
synchronized (lock) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
lock = "change lock";
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final ChangeLock changeLock = new ChangeLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
},"t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
因为锁对象更改了,所以两个线程同时执行。
七、一个对象的属性发生改变,不会影响锁对象
package com.louis.线程;
/**
* 同一对象属性的修改不会影响锁的情况
* @author alienware
*
*/
public class ModifyLock {
private String name ;
private int age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void changeAttributte(String name, int age) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始");
this.setName(name);
this.setAge(age);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: "
+ this.getName() + ", " + this.getAge());
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final ModifyLock modifyLock = new ModifyLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("张三", 20);
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("李四", 21);
}
},"t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}