Synchornized
1.用来控制对类成员变量的访问: 每个类实力都对应一把锁,每个Synchornized方法都必须获得调用该方法的类实例的所才可以进行执行,否则所属线程阻塞,方法一旦执行,则独占该锁,直到该方法执行完毕,其他线程才能获取该锁。这种机制保证了同一时刻对于每一个类的实例。其所有生命为Synchornized的成员方法之多只有一个处于执行状态,因为只有一个可以获取该实例对应的锁,从而可以避免类成员的访问冲突(只有Synchornized修饰的方法才遵守这个规则)。
在java中,类和类实例(对象)都有一把锁。
代码段的同步获取的是显式声明的对象的锁。
修饰非静态方法的时候,获取的是调用该方法的对象的锁。
修饰静态方法时,获取的时该类的字节码文件,也就是class类的锁。
通信
主要通过Object的wait()和notify()方法进行实现。
wait方法,当前线程必须拥有此对象的锁。该线程发布对该对象的锁的拥有权,并等待执行,知道其他线程通过notify方法或notifyAll方法通知才此对象的监视器上等待的线程醒来。然后该线程等到重新获得对监视器的所有权后才能继续执行。线程通过调用wait()方法,在对象的监视器上等待。
notify方法,唤醒再次对象监视器上等待的单个线程。如果多个线程在此对象上等待,则会选择唤醒其中的一个,选择时任意性的,并在对实现做出决定时发生。
总结
多线程编程时,尽量将同步放在类的内部完成,而不是通过线程来进行同步,这样可以简化编程难度。
在Synchornized方法内部通过while()增加一次判断,增加程序的严谨性。因为在没有被通知、中断或超时的情况下,线程还可以唤醒一个所谓的虚假唤醒。
package com.cyan.synchornized;
/**
* @description: 先执行sub,初次进入的时候,标志位true,所以直接打印下面的sub打印。当然再次过程中还会执行main,但是会执行main的wait。
* 当sub打印完成,则会将标志置为false. 此时当sub执行时,会进行阻塞,所以会使main()打印
*/
public class SynchornizedDamo {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 50; i++) {
business.sub(i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
business.main(i);
}
}
}).start();
}
}
/**
*@Description: 解决多线程问题时,尽量将线程同步放在类中解决,而不是在多线程中同步
*/
class Business{
private boolean isRunning = true;
public synchronized void sub(int n) {
while (!isRunning) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.out.println("sub:" + i + " loop of :" + n);
}
isRunning = false;
this.notify();
}
public synchronized void main(int n) {
while (isRunning) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 100; i++) {
System.out.println("main : " + i + " loop of: " + n);
}
isRunning = true;
this.notify();
}
}