一、synchronized
1.synchronized & 非静态方法
服务类
public class MyService{
private int count;
synchronized public void methodA() {
count ++;
}
}
线程A
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodA();
}
}
线程B
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodA();
}
}
调用类
public class Main {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
ThreadB threadB = new ThreadB(myService);
threadA.start();
threadB.start();
}
}
说明:
synchronized加在方法体上,表示当前只允许一个线程调用该对象的synchronized方法。即只有threadA 或threadB 一个线程进入methodA方法,另外一个线程需要等其他线程执行完后才能进入。
注意1:
public class Main {
public static void main(String[] args) {
MyService myServiceA = new MyService();
MyService myServiceB = new MyService();
ThreadA threadA = new ThreadA(myServiceA);
ThreadB threadB = new ThreadB(myServiceB);
threadA.start();
threadB.start();
}
}
如果将调用方法改为以上这样,那么threadA 和threadB 将失去同步效果,因为两个线程分别持有的是不同对象的锁。
注意2:
public class MyService{
private int count;
synchronized public void methodA() {
count ++;
}
synchronized public void methodB() {
count ++;
}
public void methodC() {
count ++;
}
}
同一时刻,只有一个线程进入methodA或methodB,但是进入methodC不受约束。即一个线程进入了methodA,那么另一个线程进入不了methodA也进入不了methodB,但是methodC可以随意。
2.synchronized & 静态方法
public class MyService {
synchronized static public void methodA() {
int count = 0;
}
synchronized static public void methodB() {
int count = 0;
}
}
说明:
synchronized static是锁定当前的类,而不再是锁定由该类创建的对象。这时以下的调用方式threadA和threadB就是同步的。
public class Main {
public static void main(String[] args) {
MyService myServiceA = new MyService();
MyService myServiceB = new MyService();
ThreadA threadA = new ThreadA(myServiceA);
ThreadB threadB = new ThreadB(myServiceB);
threadA.start();
threadB.start();
}
}
3.synchronized & 代码块
public class Block {
private int count;
public void methodA() {
synchronized(this){
count ++;
}
}
private String lock = "abc";
public void methodB() {
synchronized(lock){
count ++;
}
}
}
说明:
synchronized(this)代码块与synchronized方法是一样的效果,都是锁定当前对象。而synchronized(lock)是锁定String lock = "abc"这个对象。
注意:
如果一个线程锁定是lock = "abc",而另一个线程将lock = "abc"改为lock = "def",则两个线程锁定的是两个对象,达不到同步的效果。
二、volatile
线程类:
public class MyThread extends Thread{
volatile private boolean isRun = true;
public boolean isRun() {
return isRun;
}
public void setRun(boolean run) {
isRun = run;
}
@Override
public void run(){
while (isRun){
}
}
}
调用类
public class Main {
public static void main(String[] args){
try {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(10000);
myThread.setRun(false);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
说明:
如果去掉MyThread中isRun属性的volatile字段,如果程序运行在-server模式的JVM中,那么Main会陷入死循环,因为myThread.setRun(false)所设置的isRun与正在运行的线程中的isRun不是同一个。废话不多说,看图↓↓↓
线程在-server模式的JVM中运行时,首先从主内存中读取数据保存到线程私有堆栈中,然后该线程在私有堆栈中对该数值进行操作,最后再将私有堆栈中的值保存到主内存中。
因此在多线程同时访问主存中同一个值时,会造成私有堆栈与公共堆栈中的值不同步的问题,而volatile关键字就是为了解决这个问题而存在的。
当某变量声明为volatile,则线程在读取该变量时,会强制的从公共堆栈中取值。
三、wait() & notify()
线程A
public class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程B
public class ThreadB extends Thread {
private Object lock;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
lock.notify();
}
}
}
说明
wait()和notify()都是Object类的方法,线程调用wait()和notify()方法前都要获得对象级别锁,某线程调用wait()会释放锁,使当前线程停止运行,而调用notify()方法是使停止的线程重新运行。
注意
执行notify()方法后,当前线程不会立刻释放对象锁,呈wait()状态的线程也不能马上获取对象锁,要等到执行notify()方法的线程将程序执行完,当前的线程才会释放锁。