同步和异步
当访问共享资源时,我们为了保证线程安全,让每个线程在操作共享变量前读到的都是正确的值,必须同步地执行代码,即每个线程方法执行完毕后才能继续。
而异步则不需要等待其他线程的方法执行完毕,可以立刻执行,这里不涉及对共享资源的操作。
对象锁的同步和异步
直接上代码吧。
import static java.lang.Thread.sleep;
public class SyncAndAsyn {
public static void main(String[] args) {
Exec exec = new Exec();
Run1 run1 = new Run1(exec);
Run2 run2 = new Run2(exec);
Thread t1 = new Thread(run1);
Thread t2 = new Thread(run2);
t1.start();
t2.start();
}
}
class Exec{
public synchronized void func1(){
System.out.println("func 1: " + System.currentTimeMillis());
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void func2(){
System.out.println("func 2: " + System.currentTimeMillis());
}
}
class Run1 implements Runnable{
private Exec exec;
public Run1(Exec exec){
this.exec = exec;
}
@Override
public void run() {
exec.func1();
}
}
class Run2 implements Runnable{
private Exec exec;
public Run2(Exec exec){
this.exec = exec;
}
@Override
public void run() {
exec.func2();
}
}
在Exec类中,func1和func2两个方法都定义为Sunchronized,两个线程t1和t2分别执行func1和func2。这是两个线程执行两个不同的方法,应该是异步执行的吧,然而输出如下:
func 1: 1537014956574
func 2: 1537014958576
两个方法的执行时间点刚好相差了两秒,也就是说线程t2受到了线程t1执行func1方法时的sleep(2000)的影响,等t1执行func1完毕后才进入到func2里。
如果把func2定义为非Synchronized会怎么样呢。
import static java.lang.Thread.sleep;
public class SyncAndAsyn {
public static void main(String[] args) {
Exec exec = new Exec();
Run1 run1 = new Run1(exec);
Run2 run2 = new Run2(exec);
Thread t1 = new Thread(run1);
Thread t2 = new Thread(run2);
t1.start();
t2.start();
}
}
class Exec{
public synchronized void func1(){
System.out.println("func 1: " + System.currentTimeMillis());
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void func2(){
System.out.println("func 2: " + System.currentTimeMillis());
}
}
class Run1 implements Runnable{
private Exec exec;
public Run1(Exec exec){
this.exec = exec;
}
@Override
public void run() {
exec.func1();
}
}
class Run2 implements Runnable{
private Exec exec;
public Run2(Exec exec){
this.exec = exec;
}
@Override
public void run() {
exec.func2();
}
}
输出:
func 1: 1537016097712
func 2: 1537016097712
此时两个线程同时执行了func1和func2两个方法。
产生这种现象的原因是创建两个线程的Runnable对象时使用了同一个Exec对象,也就是说虽然是两个线程,但是它们执行的func1和func2是同一个对象的两个方法。而这里不存在static方法和变量,所以给func1或func2加了同步锁,实际上就是给Exec对象加了锁。两个线程访问同一个加了锁的对象,当然会同步执行。
那么另一个问题来了,为什么func1为Synchronized、func2为非Synchronized时,两个线程没有同步执行?这是因为虽然加了对象锁,但是这个锁只对对象内的Synchronized方法有效。这就好比一个超市里只有一个货架和一个收银台。同一时刻只能有一个人在收银台结账,所以结账的方法是Synchronized的,但是同时可以有很多人在货架前挑选商品(忽略最大容纳人数等极端情况),那么挑选商品的方法就不是Synchronized的。