/**
* 线程加锁
*/
public class SynchronizedDemo2 {
//静态成员变量 在主内存中
static int i;
//静态成员方法
public static void add(){
synchronized (SynchronizedDemo2.class){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int j = 0; j < 100; j++) {
add();
}
});
Thread t2 = new Thread(()->{
for (int j = 0; j < 100; j++) {
add();
}
});
//启动t1线程
t1.start();
//启动t2线程
t2.start();
//休眠main线程,让其他线程优先执行
TimeUnit.SECONDS.sleep(1);
//加锁后 不会出现值不同步的情况 每次运行结果都是200
System.out.println(i);
}
}
加锁后以上程序可能出现的执行过程
t1线程 t2线程
时刻1 抢锁且成功 为就绪态
时刻2 从主内存中复制共享变量到t1线程的私有内存中 为就绪态
时刻3 对私有内存中的变量进行逻辑处理 为就绪态
时刻4 时间片到 运行状态
时刻5 就绪状态 抢时间片,但是没有抢到,该线程处于阻塞状态, 直到时间片结束
时刻6 时间片到,运行状态,将处理后的结果写入主内存中 就绪
时刻7 退出临界区,释放锁 抢时间片,并且抢到时间片,开始执行
/**
* 如果多个线程分别持有自己的锁,则加锁没有意义
* 锁应是唯一的,体现互斥性
*/
public class SynchronizedDemo3 {
//静态成员变量 在主内存中
static int i;
//静态成员方法
public static void add(){
i++;
}
public static void main(String[] args) throws InterruptedException {
//注意,锁可以是任意对象
//定义两个锁
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
synchronized(lock1){
//为某个具体操作加锁 而不是所有代码
add();
}
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 100; i++) {
synchronized(lock2){
add();
}
}
});
//启动t1线程
t1.start();
//启动t2线程
t2.start();
//休眠main线程,让其他线程优先执行
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
}
/**
* 证明线程处于休眠状态时,不会释放锁。
*/
public class SynchronizedDemo4 {
public static void main(String[] args) throws InterruptedException {
//注意,锁可以是任意对象
Object lock = new Object();
Thread t1 = new Thread(()->{
synchronized(lock){
System.out.println("t1线程获得锁");
try {
System.out.println("t1线程开始休眠");
TimeUnit.SECONDS.sleep(10);
System.out.println("t1线程结束休眠");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2 = new Thread(()->{
synchronized(lock){
Thread thread = Thread.currentThread();
System.out.println("线程t2获得锁");
for (int i = 0; i < 50; i++) {
//执行t2线程的条件是 线程2抢占锁成功 并且分配到时间片
//若在t1线程休眠过程中 t2线程执行该语句 则说明线程在休眠过程中会释放锁,反之则不会
System.out.println(thread.getName()+"**********"+i);
}
}
System.out.println("线程t2释放锁");
});
//启动t1线程
t1.start();
TimeUnit.SECONDS.sleep(1);
//启动t2线程
t2.start();
//休眠main线程
TimeUnit.SECONDS.sleep(3);
//获取t2的状态
System.out.println(t2.getState());//block
//结论:线程在休眠过程中不会释放锁
}
}
synchronized修饰类方法时,锁时当前类对象即类名.class,当synchronized修饰实例方法时,锁时当前对象即this。
/**
* synchronized修饰类方法 当前类对象作为锁
*/
public class SynchronizedDemo5 {
//静态成员变量 在主内存中
static int i;
//静态成员方法
public synchronized static void add(){
i++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
add();
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 100; i++) {
add();
}
});
//启动t1线程
t1.start();
//启动t2线程
t2.start();
//休眠main线程,让其他线程优先执行
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
}
/**
* synchronized修饰实例方法 线程对应的对象作为锁对象
* 不存在锁竞争,因此在临界区也不会存在互斥性
*/
public class SynchronizedDemo6 {
static int i = 0;
public static void add(){
i++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public synchronized void run() {
for (int i = 0; i < 100; i++) {
add();
}
}
}
);
Thread t2 = new Thread(new Runnable() {
@Override
public synchronized void run() {
for (int i= 0; i < 100; i++) {
add();
}
}
}
);
//启动t1线程
t1.start();
//启动t2线程
t2.start();
//休眠main线程,让其他线程优先执行
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
}
synchronized实现原理
monitorenter指令时会尝试获取相应对象的monitor,获取规则如下:
如果monitor的进入数为0,则该线程可以进入monitor,并将monitor进入数设置为1,该线程即为monitor的拥有者。
如果当前线程已经拥有该monitor,只是重新进入,则进入monitor的进入数加1,所以synchronized关键字实现的锁是可重入的锁。
如果monitor已被其他线程拥有,则当前线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor。
monitorexit:
只有拥有相应对象的monitor的线程才能执行monitorexit指令。每执行一次该指令monitor进入数减1,当进入数为0时当前线程释放monitor,此时其他阻塞的线程将可以尝试获取该monitor。
synchronized的内存语义
1. 进入synchronized块的内存语义是把在synchronized块内使用到的变量从线程的工作内存中清除,这样在synchronized块内使用到该变量时就不会从线程的工作内存中获取,而是直接从主内存中获取。
2. 退出synchronized块的内存语义是把在synchronized块内对共享变量的修改刷新到主内存。
2、今天没学会什么