synchronized和volatile使用
Posted vincentyw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了synchronized和volatile使用相关的知识,希望对你有一定的参考价值。
synchronized和volatile
volatile :保证内存可见性,但是不保证原子性;
synchronized:同步锁,既能保证内存可见性,又能保证原子性;
synchronized实现可重入锁 (1.持有同一锁自动获取 2.继承锁)
锁定的对象有两种:1.类的实例(对象锁) 2.类对象(类锁)
对象锁(synchronized修饰普通方法或代码块) 对象锁已被其他调用者占用,则需要等待此锁被释放
/**
* 对象锁的两种方式
*/
//方式一
private int count =10;
public synchronized void test01() {
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
//方式二
public void test02() {
synchronized(this) {
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
}
类锁(synchronized修饰静态方法) 所有类实例化对象互斥拥有一把类锁
private static int count =10; /** * 类锁两种表现方式 */ public static synchronized void test01() { count -- ; System.out.println(Thread.currentThread().getName()+"count="+count); } public static void test02() { synchronized(CurrentDemo01.class) { count -- ; System.out.println(Thread.currentThread().getName()+"count="+count); } }
同一字符串常量代表同一把锁对象
//t1执行结束,t2再执行,t1和t2持有同一把锁 public class CurrentDemo03 { String s1 = "yew"; String s2 = "yew"; public void test01(){ synchronized (s1){ System.out.println(Thread.currentThread().getName()+"---start"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { System.out.println("interrupt"); } System.out.println(Thread.currentThread().getName()+"---end"); } } public void test02(){ synchronized (s2){ System.out.println(Thread.currentThread().getName()+"---start"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("interrupt"); } System.out.println(Thread.currentThread().getName()+"---end"); } } public static void main(String[] args) { CurrentDemo03 demo3 = new CurrentDemo03(); new Thread(demo3::test01,"t1").start(); new Thread(demo3::test02,"t2").start(); } }
synchronized同步代码块粒度越小,执行效率越高
public class CurrentDemo04 { //总数 private static int count = 0; public synchronized void test01() throws InterruptedException { System.out.println(Thread.currentThread().getName()+"---start"); TimeUnit.SECONDS.sleep(2); for (int i = 0; i <10000000 ; i++) { count++ ; } TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"---end"); } public void test02() throws InterruptedException { System.out.println(Thread.currentThread().getName()+"---start"); TimeUnit.SECONDS.sleep(2); synchronized (this){ for (int i = 0; i <10000000 ; i++) { count++ ; } } TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"---end"); } //demo04.test01();8043ms // demo04.test02();4116ms public static void main(String[] args){ CurrentDemo04 demo04 = new CurrentDemo04(); long start = System.currentTimeMillis(); new Thread(new Runnable() { @Override public void run() { try { demo04.test02(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { demo04.test02(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); System.out.println(Thread.activeCount()); while(Thread.activeCount()>2){ Thread.yield(); } long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start)+"ms"); System.out.println(count); } }
synchronized方法正常返回或者抛异常而终止,jvm会自动释放对象锁(捕获异常则不会自动释放)
public class CurrentDemo08 { //总数值 private int count = 0; public synchronized void test01() throws Exception { System.out.println(Thread.currentThread().getName()+"---start"); while (true){ count++ ; TimeUnit.SECONDS.sleep(1); if(count <5){ System.out.println(count); }else{ try { int m = count/0; }catch (Exception e){ System.out.println("ERROR :"+e.getMessage()); // return; } } } } public synchronized void test02() throws InterruptedException { System.out.println(Thread.currentThread().getName()+"----start"); TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName()+"---end"); } public static void main(String[] args) { CurrentDemo08 demo08 = new CurrentDemo08(); new Thread(()-> { try { demo08.test01(); } catch (Exception e) { System.out.println("test01中断"); } },"t1").start(); new Thread(()-> { try { demo08.test02(); } catch (InterruptedException e) { System.out.println("test02中断"); } },"t2").start(); } }
/** * @author yew * @date on 2019/11/18 - 11:49 * 一道面试题:实现一个容器,提供两个方法,add,size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数, * 当个数到5个时,线程2给出提示并结束线程2 * 1.无volatile,线程间不可见,线程t2不会结束 * 2.volatile可以保证原子的可见性,存在不确定性 while(true) 占用CPU资源 */ public class CurrentDemo11 { //List<Object> lists = new ArrayList<Object>(); volatile List<Object> lists = new ArrayList<Object>(); public void add(Object s) { lists.add(s); } public int size() { return lists.size(); } public static void main(String[] args) { CurrentDemo11 demo11 = new CurrentDemo11(); new Thread(() -> { for (int i = 0; i < 10; i++) { demo11.add(new Object()); System.out.println("add Object" + (i + 1)); try { TimeUnit.MILLISECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t1线程结束"); }, "t1").start(); new Thread(() -> { System.out.println("t2线程开始"); while (true) { if (demo11.size() == 5){ break; } } System.out.println("t2线程结束"); }, "t2").start(); } }
优化上述问题:
/**
* @author yew
* 1.wait----notify(随机唤醒持有当前锁且等待的某个线程)/notifyAll(唤醒持有当前锁所有的等待线程)
* notify随机唤醒持有锁等待的线程,但是不会释放当前持有的锁 所以监控线程不会立马结束
* 2.countdownLatch
*/
public class CurrentDemo12 {
List lists = new ArrayList();
public int size() {
return this.lists.size();
}
public void add(Object obj) {
this.lists.add(obj);
}
public static void main(String[] args) {
CurrentDemo12 demo12 = new CurrentDemo12();
Object lock = new Object();
//先启用t2进行监听
new Thread(()->{
synchronized (lock){
System.out.println("t2线程启动");
if(demo12.size() != 5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2线程结束");
lock.notify();
}
},"t2").start();
new Thread(()->{
synchronized (lock){
System.out.println("t1线程启动");
for (int i = 0; i < 10 ; i++) {
System.out.println("add Object"+(i+1));
demo12.add(new Object());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(demo12.size() == 5){
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("t1线程结束");
}
}).start();
}
}
/** * 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法, * 能够支持两个生产者线程以及10个消费者线程的阻塞调用 * wait notifyAll */ public class CurrentDemo13 { private static final int MAX = 20; private final LinkedList<Object> list = new LinkedList<>(); static int count; public synchronized void put(Object obj){ while (list.size() == MAX){ try { this.wait(); } catch (InterruptedException e) { System.out.println(); } } list.add(obj); ++count; System.out.println("生产后剩余数量"+count); this.notifyAll(); } public synchronized Object get(){ while (list.size()==0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object t = list.removeFirst(); --count; System.out.println("消费后剩余数量"+count); this.notifyAll(); return t; } public int getCount(){ return count; } public static void main(String[] args) { CurrentDemo13 demo13 = new CurrentDemo13(); //创建生产者 for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { while (true){ demo13.put(Thread.currentThread().getName()+"---"+count); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { while (true){ System.out.println(demo13.get()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } }
/**
* condition:在某种状态下
*/
public class CurrentDemo14 {
LinkedList<Object> lists = new LinkedList<>();
private static final int MAX = 20;
private static int count = 0;
ReentrantLock lock = new ReentrantLock();
Condition producer = lock.newCondition();
Condition consumer = lock.newCondition();
public void put(Object obj){
try {
lock.lock();
while (lists.size()==MAX){
producer.await();
}
lists.add(obj+"----"+count);
count++;
System.out.println("生产后剩余:"+count);
Thread.sleep(500);
consumer.signalAll();
} catch (Exception e) {
System.out.println("生产线程异常中断"+e.getMessage());
}finally {
lock.unlock();
}
}
public Object get(){
Object t = null;
try {
lock.lock();
while (lists.size()==0){
consumer.await();
}
t = lists.removeFirst();
System.out.println("已消费对象:"+t);
count--;
System.out.println("消费后剩余:"+count);
Thread.sleep(2000);
producer.signalAll();
} catch (Exception e) {
System.out.println("消费线程异常中断"+e.getMessage());
}finally {
lock.unlock();
return t;
}
}
public static void main(String[] args){
CurrentDemo14 currentDemo14 = new CurrentDemo14();
for (int i = 0; i < 2; i++) {
new Thread("p"+i){
public void run(){
for (;;) {
currentDemo14.put(Thread.currentThread().getName());
}
}
}.start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
new Thread("c"+i){
public void run(){
for (;;) {
currentDemo14.get();
}
}
}.start();
}
}
}
/** * ReentrantLock * ReentrantLock同synchronized效果相同 * 1.synchronized遇到异常,自动释放锁,reentranLock需要手动释放锁,所以释放锁需要放在finally代码块执行; * 2.tryLock() 尝试拿锁,拿不到锁的时候可以根据返回的boolean值来决定是否继续执行 * 3.lockInterruptibly()可以对线程中断做出反应 */ public class CurrentDemo15 { ReentrantLock lock = new ReentrantLock(); public void test01(){ try{ lock.lock(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"test 01"); int t = 10/0; }catch (Exception e){ System.out.println("ERROR"); }finally { lock.unlock(); } } public void test02(){ try{ // lock.tryLock(3, TimeUnit.SECONDS); // lock.lockInterruptibly(); lock.lock(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"test 02"); }catch (Exception e){ System.out.println("ERROR"); }finally { lock.unlock(); } } public static void main(String[] args) { CurrentDemo15 currentDemo15 = new CurrentDemo15(); new Thread("t1"){ public void run(){ currentDemo15.test01(); } }.start(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } Thread t2 = new Thread("t2"){ public void run(){ currentDemo15.test02(); } }; t2.start(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); } }
以上是关于synchronized和volatile使用的主要内容,如果未能解决你的问题,请参考以下文章