4.volatile关键字和synchronized关键字


volatile关键字

作用:volatile修饰的共享变量保证了不同线程对其进行操作时的可见性。


问题场景:主线程修改stop为true之后thread线程可能停不下来

public class VolatileDemo {
    private static boolean stop = false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("thread start");
                while(!stop) {
                }
                System.out.println("thread exit");
            }
        };
        thread.start();
        Thread.sleep(100);
        stop = true;
        System.out.println("stop set true");
    }
}

执行结果:thread结束不了 "thread exit"一直没有打印

thread  start
stop set true

用volatile修饰stop之后:程序正常结束

public class VolatileDemo {
    private static volatile boolean stop = false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("thread  start");
                while(!stop) {
                }
                System.out.println("thread  exit");
            }
        };
        thread.start();
        Thread.sleep(100);
        stop = true;
        System.out.println("stop set true");
    }
}

执行结果:

thread  start
stop set true
thread  exit

Process finished with exit code 0

第一段程序为什么停止不了:

程序执行的时候,每个线程在运行过程中都有自己的工作内存,当thread线程运行的时候会将stop变量的值拷贝一份房在自己的内存中,当main线程更改了stop的值之后没有及时更新主存中的值。thread线程不知道stop的变量已经更改所以会一直循环下去。

volatile修饰之后:

  • 1.使用volatile关键字会强制将修改的值立即写入主存;
  • 2.当main线程进行修改时,会导致thread的工作内存中缓存变量stop的缓存行无效
  • 3.由于thread中缓存变量stop的缓存行无效,所以当线程再次读取变量stop的值时会去主存重新读取。
    volatile修饰之后保证了stop变量修改之后thread线程能够读取到新的值所以程序能够正常退出。

volatile关键字的两层语义

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  2)禁止进行指令重排序。

synchronized关键字

synchronized关键字可以用来对程序加锁。常用的三种形式:
  1. 修饰一个代码块。
  2. 修饰一个方法。
  3. 修改一个静态的方法。
  

1. 修饰一个代码块

修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象,锁(lock)可以是任一对象,同一把锁同一时间只能由一个线程可以获得。

synchronized(lock){
	 //do something
}

2. 修饰一个方法

修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。

public synchronized void method(){//do something}

效果等同于

public void method(){
	synchronized(this) {
		//do something
	}
}

3. 修改一个静态的方法

修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。

public synchronized static void method(){}

效果等同于

public static void method(){
	synchronized(类名.class) {
		//do something
	}
}

4. 不同锁对象的区别

synchronized(this){…}、synchronized(类名.class){…}与synchronized(任意对象){…} 的区别

  1. synchronized(this){…}
    this关键字指向的是当前对象的引用,同一个实例内用同一把锁,用的是同一个对象就是同一把锁。
public class SynchronizedDemo {
   public static void main(String[] args) {
       ThisDemo thisDemo = new ThisDemo();
       new Thread("t1"){
           @Override
           public void run() {
               thisDemo.print();
           }
       }.start();
       new Thread("t2"){
           @Override
           public void run() {
               thisDemo.print();
           }
       }.start();
   }

   public static class ThisDemo {
       private void print() {
           synchronized (this) {
               System.out.println(Thread.currentThread().getName() + "start");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName() + "end");
           }
       }
   }
}

输出结果 用的是同一把锁有序输出 第一个线程完成第二个才开始

t1start
t1end
t2start
t2end

Process finished with exit code 0

调整成每次new一个新对象

public class SynchronizedDemo {
    public static void main(String[] args) {
        new Thread("t1"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
        new Thread("t2"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
    }

    public static class ThisDemo {
        private void print() {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + "start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end");
            }
        }
    }
}

输出变成无序了,两个线程同时进入代码块

t1start
t2start
t2end
t1end

Process finished with exit code 0
  1. synchronized(类名.class){…}
    在程序运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。所以 在同一个jvm进程中只有一个,整个进程内用同一把锁,即使是每个线程都new一个新的对象,也是有序的,同时只有一个进程进入该代码块。
public class SynchronizedDemo {
    public static void main(String[] args) {
        new Thread("t1"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
        new Thread("t2"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
    }

    public static class ThisDemo {
        private void print() {
            synchronized (ThisDemo.class) {
                System.out.println(Thread.currentThread().getName() + "start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end");
            }
        }
    }
}

输出

t1start
t1end
t2start
t2end

Process finished with exit code 0
  1. synchronized(任意对象){…}
    当lock对象为同一个时,才能保证同一时刻只有一个线程在执行
public class SynchronizedDemo {
    private static Object lock = new Object();
    public static void main(String[] args) {
        new Thread("t1"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
        new Thread("t2"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
    }

    public static class ThisDemo {
        private void print() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end");
            }
        }
    }
}

锁对象是同一个,输出正常

t1start
t1end
t2start
t2end

Process finished with exit code 0

public class SynchronizedDemo {
    public static void main(String[] args) {
        new Thread("t1"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
        new Thread("t2"){
            @Override
            public void run() {
                new ThisDemo().print();
            }
        }.start();
    }

    public static class ThisDemo {
        private void print() {
            synchronized (new Object()) {
                System.out.println(Thread.currentThread().getName() + "start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end");
            }
        }
    }
}

锁每次都是新建的,输出是并行的,两条线程同时进入到了代码块

t1start
t2start
t1end
t2end

Process finished with exit code 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值