一、最基本用法
public class MyThread extends Thread{
private Integer count = 5;
public synchronized void run(){
count--;
System.out.println(Thread.currentThread().getName() + ":" + count);
}
public static void main(String[] args){
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread, "T1");
Thread t2 = new Thread(myThread, "T2");
Thread t3 = new Thread(myThread, "T3");
Thread t4 = new Thread(myThread, "T4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
如上,启动4个线程t1,t2,t3,t4同时访问属性count,由于方法run()被关键字synchronized修饰,所以可以保证count属性被同步访问,可以避免多个线程同时访问造成的数据错误。
二、多线程访问同一个类的多个对象相同一个方法
/**
* 多线程访问同一个类的多个对象相同一个方法
*
* @author xingguishuai
*
**/
public class MutiThread {
public synchronized void print(String name) throws InterruptedException {
Integer age;
if("Tom".equalsIgnoreCase(name)){
age = 10;
System.out.println("name:" + name + " has set age");
Thread.sleep(1000);
}else{
age = 5;
System.out.println("name:" + name + " has set age");
}
System.out.println("name:" + name + "age:" + age);
}
public static void main(String[] args){
final MutiThread m1 = new MutiThread();
final MutiThread m2 = new MutiThread();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
m1.print("tom");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
try {
m2.print("tomcat");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
}
}
如上,MutiThread两个实例m1,m2,两个线程分别去执行m1,m2的print()方法,输出结果:
name:tom has set age
name:tomcat has set age
name:tomcat age:5
name:tom age:10
可以看出两个实例的方法执行不是同步的。
如果,想要让这两个线程同步执行可以在print方法上添加static关键字。
public static synchronized void print(String name) throws InterruptedException{
....
}
执行结果:
name:tom has set age
name:tom age:10
name:tomcat has set age
name:tomcat age:5
可以看出,此时两个线程是同步访问方法print的。
三、 多线程访问同一对象的多个方法
/**
* 多线程访问同一对象的多个方法
*
* @author xingguishuai
* @create 2017-02-28-11:18
**/
public class MutiMethodThread {
public synchronized void print1() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+ " method is print1");
Thread.sleep(3000);
System.out.println("method print1 done");
}
public void print2(){
System.out.println(Thread.currentThread().getName() + " method is print2");
}
public static void main(String[] args){
final MutiMethodThread mutiMethodThread = new MutiMethodThread();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
mutiMethodThread.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread1");
Thread thread2 = new Thread(new Runnable() {
public void run() {
mutiMethodThread.print2();
}
},"thread2");
thread1.start();
thread2.start();
}
}
如上,线程thread1,thread2访问对象mutiMethodThread的print1()方法和print2()方法,其中print1方法被synchronized关键修饰,输出结果如下:
thread1 method is print1
thread2 method is print2
method print1 done
可以看出print1,print2两个方法并未同步执行。因为print2未被synchronized关键字修饰,是一个异步方法。
如果想让print1,print2两个方法同步执行,可以为print2添加synchronized关键字。
public sychronized void print2(){
System.out.println(Thread.currentThread().getName() + " method is print2");
}
再次执行,输出结果为:
thread1 method is print1
method print1 done
thread2 method is print2
可以看出此时,print1,print2两个方法是按照同步方式执行的。这是因为sychronized关键字获取的是对象锁。thread1运行时获取到了mutiMethodThread对象的锁,当thread2再尝试获取对象锁时被阻塞,等到thread1释放锁后,thread2继续执行。
当print2不被synchronized修饰时,线程2不检测对象锁的存在。所以是异步执行的。
当synchronized修饰静态方法时,是类级别的锁。
四、锁重入
可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。
五、被synchronized修饰的方法遇见异常时,会立刻释放锁。
六、synchronized的加锁对象
- 要避免使用字符串常量作为锁,因为字符串常量是常量池里的东西,在任何地方用到同样的代码就会锁住
- 当锁对象值改变时,由于变量的地址指向改变,所以锁会失效。
评论区