synchronized为java中的关键字,用于保证多线程并发时,在同一时刻最多只有一个线程执行该段代码。
包括两种用法:synchronized 方法和 synchronized 块。
第一个Demo
public class ThreadTest extends Thread {
private int threadNo;
public ThreadTest(int threadNo) {
this.threadNo = threadNo;
}
public static void main(String[] args) throws Exception {
for (int i = 1; i < 10; i++) {
new ThreadTest(i).start();
Thread.sleep(1);
}
}
@Override
public synchronized void run() {
for (int i = 1; i < 100; i++) {
System.out.println("No." + threadNo + ":" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果
No.1:1
No.2:1
No.3:1
No.4:1
No.5:1
No.6:1
No.7:1
No.8:1
No.9:1……..
很显然并没有实现同步操作,代码中在run()方法上面加了synchronized关键字,那为什么没有实现同步呢
首先明确一点,对于一个成员方法加synchronized关键字,这实际上是以这个成员方法所在的对象本身作为对象锁。
这段代码其实就是以ThreadTest类的一个具体对象作为对象锁的。一共十个线程,每个线程持有自己线程对象的那个对象锁。这必然不能产生同步的效果
第二个Demo
public class ThreadTest2 extends Thread{
private int threadNo;
private String lock = "lock";
public ThreadTest2(int threadNo) {
this.threadNo = threadNo;
}
public static void main(String[] args) throws Exception {
for (int i = 1; i < 10; i++) {
new ThreadTest2(i).start();
Thread.sleep(1);
}
}
@Override
public void run() {
synchronized (lock) {
for (int i = 1; i < 100; i++) {
System.out.println("No." + threadNo + ":" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
执行结果
No.1:1
No.1:2
No.1:3
No.1:4
No.1:5
No.1:6
No.1:7
No.1:8
No.1:9……
很显然这段代码实现了同步,在run()方法中使用了同步代码块,并且以“lock”作为锁
以一个String类型的对象为锁,根据Java方法的传值特点,lock为同一个String对象,所以该对象锁是共享且唯一的
第三个Demo
public class ThreadTest3 extends Thread{
private int threadNo;
public ThreadTest3(int threadNo) {
this.threadNo = threadNo;
}
public static void main(String[] args) throws Exception {
for (int i = 1; i < 10; i++) {
new ThreadTest3(i).start();
Thread.sleep(1);
}
}
public static synchronized void dowork(int threadNo) {
for (int i = 1; i < 100; i++) {
System.out.println("No." + threadNo + ":" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
dowork(threadNo);
}
}
执行结果
No.1:1
No.1:2
No.1:3
No.1:4
No.1:5
No.1:6
No.1:7
No.1:8…………
很显然这段代码也实现了同步~ 他和第一个demo一样是把synchronized关键字放在方法上面,唯一的不一样就是,dowork方法使用了static关键字修饰
由于在JVM中,所有被加载的类都有唯一的类对象
对于同步静态方法,对象锁就是该静态放发所在的类的Class对象,唯一的 ThreadTest3.class对象
虽然在main方法中创建了该类的多个实例,但是它的类对象只有一个,所以对象锁是共享且唯一的
总结
1、 对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作;
2、 如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的
Class对象(唯一);
3、 对于代码块,对象锁即指synchronized(abc)中的abc;
4、 静态方法则一定会同步,非静态方法需在单例模式才生效,推荐用静态方法
顺便说一下:
Java中多线程锁释放的条件:
1、执行完同步代码块,就会释放锁。(synchronized)
2、在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
3、在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。(wait)