锁的优化及注意事项

多线程存在的效率问题:各线程的元数据,上下文切换,线程调度等

1.有助于提高锁性能的几点建议

1.1 减少锁持有的时间

只在需要的代码段加锁,不要将不必要的内容加到同步代码方法或同步代码块中。

1.2 减小锁粒度

和1.1的方法的差不多,缩小锁定对象的范围,从而降低锁冲突的可能性。

1.3 用读写分离锁来替换独占锁

1.4 锁分离

与读写锁的思路类似,对于一个对象,不使用独占锁,而是依据操作功能的不同,对锁进行分离。如LinkedBlockingQueue中的take和put方法实现了取数据和读数据的分离。

1.5 锁粗化

虚拟机再遇到一连串连续地对同一个锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步的次数。我们自己写程序时也应该注意这点。但要注意和1.2的区别。

2. Java虚拟机对锁优化所做的努力

下面都是jdk对锁所做的优化,当对象偏向锁的标志位(可偏向)为1时,可以将锁设置为偏向锁,偏向锁适合只有一个线程访问的情景,轻量级锁的使用代价低于轻量级锁。如果对象的偏向锁的标志位为0时,不可以将锁设置为偏向锁,那么这时可以尝试将其设置为轻量级锁,轻量级锁适合多个线程交替竞争锁,如果多个线程争夺锁,锁会膨胀为重量级锁。

2.1 锁偏向

如果一个线程获得了锁,那么锁就进入偏向模式。当这个线程再次请求时,无需再做任何同步操作,可以节省大量有关锁申请的操作,从而提高程序性能。适合于几乎没有锁竞争的场合。

2.2 轻量级锁

https://www.cnblogs.com/paddix/p/5405678.html

https://blog.csdn.net/lengxiao1993/article/details/81568130

2.3 自旋锁

虚拟机让当前线程做几个空循环,再经过若干次循环后,如果可以得到锁,那么就顺利进入临界区,否则就挂起线程。

2.4 锁消除

锁消除时一种更彻底的优化。Java虚拟机再JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源的竞争的锁。如StringBuffer,Vector等的使用过程中可能会出现。锁消除要保证不会出现逃逸分析才可以进行。

3 ThreadLocal

除了控制资源的访问外,还可以通过增加资源来保证对象的线程安全。锁是前者的思路,ThreadLcal是后者的思路。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

3.1 ThreadLocal是什么

ThreadLocal源码中该类的介绍:该类提供线程局部变量。这些变量与它们的正常变量不同,因为每个访问一个线程的变量都有它自己的、独立的初始化拷贝。实例通常是希望将状态与线程关联的类中的私有字段(例如,*用户ID或事务ID)。

3.2 ThreadLocal原理

https://www.cnblogs.com/ldq2016/p/9041856.html 这个博客讲得不错,结合《实战java高并发程序设计》的这一小节的内容,以及自己的一些实践,应该可以理解ThreadLocal。

(1)每个线程为自己的ThreadLocal变量赋值

定义一个ThreadLocal对象,当一个线程要使用这个对象时,可以使用set方法给这个对象赋值,如果不赋值的话,而定义这个对象的时候有没有定义initialValue()方法赋予初值,这个对象的值可能为null,使用时就会出现空指针异常。所以在使用ThreadLoacl对象时,如果所有线程都是相同的值,那要在定义这个ThreadLocal变量时,给这个对象利用initialValue()方法赋予初值。如果每个线程这个变量的值不同,那要利用set()方法赋值,最好也利用initValue()方法赋值,防止空指针异常错误的出现。

《 实战java高并发程序设计》中的例子,

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadLocalDemo {
    private static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>();

    public static class ParseDate implements Runnable{
        int i=0;
        public ParseDate(int i){this.i=i;}

        public void run() {
            try{
                if(t1.get()==null){
                    t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
                }
                Date t =t1.get().parse("2015-03-29 19:29:"+i%60);
                System.out.println(i+":"+t);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String []args) throws InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(10);
        for(int i=0;i<1000;i++){
            es.execute(new ParseDate(i));
        }
        Thread.sleep(2000);
    }
}

每个线程在使用ThreadLocal对象t1时,要利用 t1.get() == null 来判断t1是否为空,如果为空,给它赋值。如果尝试将

                if(t1.get()==null){
                    t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
                }

这段代码删掉,会报空指针异常的错误。

而如果在定义t1时,设置初值,就不会报错了

private static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>(){
    protected SimpleDateFormat initialValue(){
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }
};

这里我有一个疑问,同样是使用t1,为什么有的线程为空,有的不为空,不太懂,但是自己在使用时注意初始化和set就好了。 

(2)线程取得ThreadLocal变量中的值

利用get()方法

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

从代码中可以看出,get方法首先尝试从ThreadLocalMap中获取这个线程对应的值,当为空时,尝试获取定义这个对象时的初始值,如果还为空,就返回null了。

(3)关于ThreadLocal变量GC

ThreadLocal变量中可能维护了很多线程的value,在一个线程中所以确定不用这个变量的时候,可以使用remove()方法将这个变量移除中的value移除,加快GC,防止内存泄漏。也可以使用 “ThreadLcal对象名” = null 加快将这个变量设为null,加快GC。remove源码如下:

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

 

4. 无锁

锁是一种悲观的策略,认为有多个线程会同时访问同一资源,所以有多个线程访问临界区时,只能有一个线程访问临界去资源,其他线程只能阻塞。而无锁是一种乐观的策略,假设有冲突,也不需要阻塞,而是通过CAS技术不断自旋尝试,直到成功或者放弃。典型的应用是原子类和一些并发容器。 

5. 锁的优化及注意事项

5.1 死锁是什么

5.2 哲学家问题的实例代码

public class DeadLock extends Thread {
    protected Object tool;
    static Object fork1 = new Object();
    static Object fork2 = new Object();

    public DeadLock(Object obj){
        this.tool = obj;
        if(tool == fork1){
            this.setName("哲学家A");
        }
        if(tool == fork2){
            this.setName("哲学家B");
        }
    }


    public void run() {
        if(tool == fork1){
            synchronized (fork1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (fork2){
                    System.out.println("哲学家A开始吃饭了");
                }
            }
        }
        if(tool == fork2){
            synchronized (fork2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (fork1){
                    System.out.println("哲学家B开始吃饭了");
                }
            }
        }
    }

    public static void main(String []args) throws InterruptedException {
        DeadLock 哲学家A = new DeadLock(fork1);
        DeadLock 哲学家B = new DeadLock(fork2);
        哲学家A.start();
        哲学家B.start();
        Thread.sleep(1000);
     }
}

5.3 死锁检测方法

 

结果:

5.3 死锁避免的方法:

1.使用无锁的方法

2.限制线程获得锁的顺序 

3.超时检测

4.死锁检测

https://blog.csdn.net/ls5718/article/details/51896159

5.4 死锁解除

释放资源或者撤销线程 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园的建设目标是通过数据整合、全面共享,实现校园内教学、科研、管理、服务流程的数字化、信息化、智能化和多媒体化,以提高资源利用率和管理效率,确保校园安全。 智慧校园的建设思路包括构建统一支撑平台、建立完善管理体系、大数据辅助决策和建设校园智慧环境。通过云架构的数据心与智慧的学习、办公环境,实现日常教学活动、资源建设情况、学业水平情况的全面统计和分析,为决策提供辅助。此外,智慧校园还涵盖了多媒体教学、智慧录播、电子图书馆、VR教室等多种教学模式,以及校园网络、智慧班牌、校园广播等教务管理功能,旨在提升教学品质和管理水平。 智慧校园的详细方案设计进一步细化了教学、教务、安防和运维等多个方面的应用。例如,在智慧教学领域,通过多媒体教学、智慧录播、电子图书馆等技术,实现教学资源的共享和教学模式的创新。在智慧教务方面,校园网络、考场监控、智慧班牌等系统为校园管理提供了便捷和高效。智慧安防系统包括视频监控、一键报警、阳光厨房等,确保校园安全。智慧运维则通过综合管理平台、设备管理、能效管理和资产管理,实现校园设施的智能化管理。 智慧校园的优势和价值体现在个性化互动的智慧教学、协同高效的校园管理、无处不在的校园学习、全面感知的校园环境和轻松便捷的校园生活等方面。通过智慧校园的建设,可以促进教育资源的均衡化,提高教育质量和管理效率,同时保障校园安全和提升师生的学习体验。 总之,智慧校园解决方案通过整合现代信息技术,如云计算、大数据、物联网和人工智能,为教育行业带来了革命性的变革。它不仅提高了教育的质量和效率,还为师生创造了一个更加安全、便捷和富有智慧的学习与生活环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值