1.synchronized的三种应用方式
<1>修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
<2>修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
<3>修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
synchronized修饰实例方法:
public synchronized void produceApple(Apple apple) {
while(index>=apples.length) {
//容器已满,无法继续生产苹果
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notifyAll();
//生产苹果
apples[index] =apple;
System.out.println(Thread.currentThread().getName()+"生产了编号为:"+apple.id+"的苹果");
index++;
}
//取苹果
public synchronized Apple eatApple() {
while(index<=0) {
//苹果全部消费,等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notifyAll();
//消费苹果
index--;
System.out.println(Thread.currentThread().getName()+"消费编号为:"+apples[index].id+"的苹果");
return apples[index];
}
修饰代码块方法:
public void run() {
// TODO Auto-generated method stub
if(flag) {
synchronized (zs) {
zs.say();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//同时 锁住 lisi
synchronized (ls) {
zs.get();
}
}
}
synchronized的一些作用域:
1.synchronized修饰实例方法时,多个线程同时访问该类的的实例方法,只有一个线程可以获得锁,也就算该类所有被synchronized修饰的方法只能被同一个线程访问(获得锁的线程),其他线程只能访问没有被synchronized修饰的方法,但是如果synchronized修饰静态方法当前类就是锁对象,所以该类的实例方法即使被synchronized修饰也是能被其他线程访问的;
2.得到对象锁的线程可以访问该对象中其他被synchronized修饰的方法;
3.子类的synchronized方法中可以调用父类的synchronized方法;
volatile关键字:
public class Test02 {
private boolean flag=true;
public void m(){
System.out.println("start");
while(flag){
}
System.out.println("end");
}
public static void main(String[] args) {
Test02 t2=new Test02();
new Thread(()->t2.m()).start();
try {
Thread.sleep(1000);//rangcpu空闲下来区读取数据存入缓存
} catch (Exception e) {
e.printStackTrace();
}
t2.flag=false;
}
}
结果:
start
将flag变量加上volatile后
start
end
不加volatile关键字,cpu将线程中的flag读取到自己的缓存中后,即使cpu调度其他线程来改变了它,他也不会重新将修改了的值读取到缓存,加上volatile关键字后,若值改变,就会通知cpu重新读取缓存中修改后的值
wait()会释放锁,notify不会释放锁;
在这里插入图片描述