搜索词>>自旋锁 耗时0.0020
  • Java 多线程编程自旋锁详解

    本文将讲述什么是自旋锁?自旋锁的使用场景,什么情况适合使用自旋锁?Java 怎么使用自旋锁?一、什么是自旋锁?计算机系统资源总是有限的,有些资源需要互斥访问,因此就有了锁机制,只有获得锁的线程才能访问资源。锁保证了每次只有一个线程可以访问资源。当线程申请一个已经被其他线程占用的锁,就会出现两种情况。一种是没有获得锁的线程会阻塞自己,等到锁被释放后再被唤起,这就是互斥锁;一种是没有获得锁的线程一直循环在那里看是否该锁的保持者已经释放了锁,这就是自旋锁。二、自旋锁可能引起的问题1.过多占据CPU时间:如果锁的当前持有者长时间不释放该锁,那么等待者将长时间的占据cpu时间片,导致CPU资源的浪费,因此可以设定一个时间,当锁持有者超过这个时间不释放锁时,等待者会放弃CPU时间片阻塞;2.死锁问题:试想一下,有一个线程连续两次试图获得自旋锁(比如在递归程序中),第一次这个线程获得了该锁,当第二次试图加锁的时候,检测到锁已被占用(其实是被自己占用),那么这时,线程会一直等待自己释放该锁,而不能继续执行,这样就引起了死锁。因此递归程序使用自旋锁应该遵循以下原则:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。三、JAVA 多线程编程中一种自旋锁使用案例import java.util.concurrent.atomic.AtomicReference;public class SpinLock {    //java中原子(CAS)操作    AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋锁的线程对象    private int count;    public void lock() {        Thread cur = Thread.currentThread();        //lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。当有第二个线程调用lock操作时由于owner值不为空,导致循环        //一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。        while (!owner.compareAndSet(null, cur)) {        }    }    public void unLock() {        Thread cur = Thread.currentThread();        owner.compareAndSet(cur, null);    }}上述代码说明:调用lock方法时,如果owner当前值为null,说明自旋锁还没有被占用,将owner设置为currentThread,并进行锁定。调用lock方法时,如果owner当前值不为null,说明自旋锁已经被其他线程占用,当前线程就会在while中继续循环检测。调用unlock方法时,会将owner置为空,相当于释放自旋锁。public class TestThread extends Thread {    static int sum;    private SpinLock lock;    public TestThread(SpinLock lock) {        this.lock = lock;    }    @Override    public void run() {        this.lock.lock();        sum++;        System.out.println("当前值:=====>"+sum);        this.lock.unLock();    }    public static void main(String[] args) throws InterruptedException {        SpinLock lock = new SpinLock();        for (int i = 0; i < 100; i++) {            TestThread test = new TestThread(lock);            Thread t = new Thread(test);            t.start();        }        Thread.currentThread().sleep(1000);        System.out.println(sum);    }}上述代码执行的输出结果一定是顺序的1-100输出。四、名词解释java CAS原子操作:Compare And Set 在java.util.concurrent.atomic包下很多类都具有compareAndSet方法。五、自旋锁的使用场景,什么时候适合使用自旋锁由于自旋锁只是在当前线程不停地执行循环体,不进行线程状态的切换,因此响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要占用CPU时间。如果线程竞争不激烈,并且保持锁的时间很短,则适合使用自旋锁。
  • Java多线程编程CLH锁详解

    本文将讲述CLH锁的使用场景,什么情况适合使用CLH锁?Java 怎么使用CLH锁?一、什么是CLH锁?CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。二、JAVA 多线程编程中一种CLH锁使用案例import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class CLHLock {     public static class CLHNode {         private volatile boolean isLocked = true; // 默认是在等待锁     }     @SuppressWarnings("unused" )     private volatile CLHNode tail ;     private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater                   . newUpdater(CLHLock.class, CLHNode .class , "tail" );     public void lock(CLHNode currentThread) {         CLHNode preNode = UPDATER.getAndSet( this, currentThread);         if(preNode != null) {//已有线程占用了锁,进入自旋             while(preNode.isLocked ) {             }         }     }     public void unlock(CLHNode currentThread) {         // 如果队列里只有当前线程,则释放对当前线程的引用(for GC)。         if (!UPDATER .compareAndSet(this, currentThread, null)) {             // 还有后续线程             currentThread. isLocked = false ;// 改变状态,让后续线程结束自旋         }     } }​​​​​​​三、CLH锁 与 MCS锁 的比较代码段 小部件
  • Java多线程编程MCS锁详解

    本文将讲述MCS锁的使用场景,什么情况适合使用MCS锁?Java 怎么使用MCS锁?一、什么是MCS锁?MCS Spinlock 是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。二、JAVA 多线程编程中一种MCS锁使用案例import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class MCSLock {     public static class MCSNode {         volatile MCSNode next;         volatile boolean isBlock = true; // 默认是在等待锁     }     volatile MCSNode queue;// 指向最后一个申请锁的MCSNode     private static final AtomicReferenceFieldUpdater UPDATER = AtomicReferenceFieldUpdater             .newUpdater(MCSLock.class, MCSNode.class, "queue");     public void lock(MCSNode currentThread) {         MCSNode predecessor = UPDATER.getAndSet(this, currentThread);// step 1         if (predecessor != null) {             predecessor.next = currentThread;// step 2             while (currentThread.isBlock) {// step 3             }         }else { // 只有一个线程在使用锁,没有前驱来通知它,所以得自己标记自己为非阻塞                currentThread. isBlock = false;           }     }     public void unlock(MCSNode currentThread) {         if (currentThread.isBlock) {// 锁拥有者进行释放锁才有意义             return;         }         if (currentThread.next == null) {// 检查是否有人排在自己后面             if (UPDATER.compareAndSet(this, currentThread, null)) {// step 4                 // compareAndSet返回true表示确实没有人排在自己后面                 return;             } else {                 // 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者                 // 这里之所以要忙等是因为:step 1执行完后,step 2可能还没执行完                 while (currentThread.next == null) { // step 5                 }             }         }         currentThread.next.isBlock = false;         currentThread.next = null;// for GC     } }
  • Java多线程编程排队锁(Ticket Lock)详解

    本文将讲述排队锁的使用场景,什么情况适合使用排队锁?Java 怎么使用排队锁?一、什么是排队锁Ticket Lock?Ticket Lock 是为了解决自旋锁的公平性问题,类似于现实中银行柜台的排队叫号:锁拥有一个服务号,表示正在服务的线程,还有一个排队号;每个线程尝试获取锁之前先拿一个排队号,然后不断轮询锁的当前服务号是否是自己的排队号,如果是,则表示自己拥有了锁,不是则继续轮询。当线程释放锁时,将服务号加1,这样下一个线程看到这个变化,就退出自旋。二、JAVA 多线程编程中一种排队锁Ticket Lock使用案例import java.util.concurrent.atomic.AtomicInteger;public class TicketLock {   private AtomicInteger serviceNum = new AtomicInteger(); // 服务号   private AtomicInteger ticketNum = new AtomicInteger(); // 排队号   public int lock() {         // 首先原子性地获得一个排队号         int myTicketNum = ticketNum.getAndIncrement();              // 只要当前服务号不是自己的就不断轮询       while (serviceNum.get() != myTicketNum) {       }       return myTicketNum;    }    public void unlock(int myTicket) {        // 只有当前线程拥有者才能释放锁        int next = myTicket + 1;        serviceNum.compareAndSet(myTicket, next);    }}三、排队锁Ticket Lock缺点Ticket Lock 虽然解决了公平性的问题,但是多处理器系统上,每个进程/线程占用的处理器都在读写同一个变量serviceNum ,每次读写操作都必须在多个处理器缓存之间进行缓存同步,这会导致繁重的系统总线和内存的流量,大大降低系统整体的性能。后续将会介绍的CLH锁和MCS锁,解决这个问题。
  • java多线程编程_java多线程安全_java多线程实现安全锁CAS机制

    java多线程编程_java多线程安全_java多线程实现安全锁CAS机制,CAS在java多线程中相当于数据库的乐观锁,synchronized相当于数据库的乐观锁。<h2>引言</h2>     java多线程编程中难免会遇到资源共享。这里将会讲解一下java多线程中的CAS机制和锁的基础概念。java多线程编程_java多线程安全_java多线程实现安全锁CAS机制,CAS在java多线程中相当于数据库的乐观锁,synchronized相当于数据库的乐观锁。 <h2>一.java多线程编程常见问题</h2> <strong><a rel="external nofollow" target="_blank" id="java多线程1" name="java多线程1">java多线程示例程序</a>:</strong>启动两个线程,每个线程中让静态共享变量count循环累加100次<br /> <br /> <a rel="external nofollow" target="_blank" id="java多线程代码" name="java多线程代码"><strong>java多线程示例代码</strong></a>: <pre> <code class="language-java">package xqlee.net.project.demo.thread.synchronize; public class ForTest { public static int count = 0; public static void main(String[] args) { // 开启两个线程 for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程累计加100 for (int j = 0; j < 100; j++) { count++; // System.out.println(count); } } }).start(); } try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println("count=" + count); } } </code></pre> <strong>疑惑</strong>:<span style="color:#6600ff">最终输出的count结果是多少呢?两个线程执行完毕一定是200吗?</span><br /> <br /> <strong>答案</strong>:因为这段代码不是线程安装的,所以最终的自增结果很有可能会小于200。<br /> <strong>尝试执行结果</strong>:<br /> <img alt="java多线程演示代码执行结果" class="img-thumbnail" src="/assist/images/blog/00053be4566b4b93acc2f6e7b13e93bf.png" /><br /> 接下来,修改上面的演示代码。通过关键字synchronized在java多线程编程实现中添加一个锁。这个是个重量级的锁。后续详说。<br /> 修改后演示代码如下: <pre> <code class="language-java">package xqlee.net.project.demo.thread.synchronize; public class ForTest { public static int count = 0; public static void main(String[] args) { // 开启两个线程 for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程累计加100 for (int j = 0; j < 100; j++) { synchronized (ForTest.class) { count++; } // System.out.println(count); } } }).start(); } try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println("count=" + count); } } </code></pre> <br /> 再次执行演示代码,查看结果:<br /> <img alt="java多线程编程加锁后的执行结果" class="img-thumbnail" src="/assist/images/blog/0ef74f747d6c440c8f62513431d9b462.png" /><br />     加了同步锁之后,count自增的操作变成了原子性操作,所以最终的输出一定是<strong>count=200</strong>,代码实现了线程安全。<br /> <br />     但是注意,虽然synchronized确保了线程的安全,但是在某些情况下,却不是一个最优选择。为啥这么说呢?关键在于synchronized存在性能问题。synchronized关键字会让没有得到锁资源的线程进入BLOCKED状态,而后再争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到操作系统<strong>用户模式</strong>和<strong>内核模式</strong>的转换,代价比较高。尽管Java1.6为Synchronized做了优化,增加了从<strong>偏向锁</strong>到<strong>轻量级锁</strong>再到<strong>重量级锁</strong>的过度,但是在最终转变为重量级锁之后,性能仍然较低。<br /> <br /> <br /> 那么有啥好的其他解决办法嘛?<br /> 有:原子操作类;<br /> <img alt="JDK中的原子操作类" class="img-thumbnail" src="/assist/images/blog/4a065fca722e4577982931e44fe71839.png" /> <p>  所谓原子操作类,指的是java.util.concurrent.atomic包下,一系列以Atomic开头的包装类。例如<strong>AtomicBoolean</strong>,<strong>AtomicInteger</strong>,<strong>AtomicLong</strong>。它们分别用于Boolean,Integer,Long类型的原子性操作。</p> <p>现在我们尝试在代码中引入AtomicInteger类:</p> <pre> <code class="language-java">package xqlee.net.project.demo.thread.synchronize; import java.util.concurrent.atomic.AtomicInteger; public class ForTest2 { public static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) { // 开启两个线程 for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程累计加100 for (int j = 0; j < 100; j++) { count.incrementAndGet(); // System.out.println(count); } } }).start(); } try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println("count=" + count); } } </code></pre> <br /> 演示执行结果:<br /> <img alt="原子操作类进行乐观锁" class="img-thumbnail" src="/assist/images/blog/be44242a5e9340abbe97aa8cd621a55f.png" /><br /> 使用AtomicInteger之后,最终的输出结果同样可以保证是200。并且在<strong>某些情况下</strong>,代码的性能会比Synchronized更好。<br /> <br /> 那么Atomic操作类底层到底利用了什么手段呢?<br /> 其实Atomic就是用到了我们要讲的[CAS机制]<br />   <p><strong>什么是CAS?</strong></p> <p> </p> <p>CAS是英文单词<strong>Compare And Swap</strong>的缩写,翻译过来就是比较并替换。</p> <p> </p> <p>CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。</p> <p>更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。</p> <p>这样说或许有些抽象,我们来看一个例子:</p> <p>1.在内存地址V当中,存储着值为10的变量。</p> <img alt="内存地址V" class="img-thumbnail" src="/assist/images/blog/9da6331c557f46d3ad148fe777a91e5a.png" /><br /> 2.此时线程1想要把变量的值增加1。对线程1来说,旧的预期值A=10,要修改的新值B=11。<br /> <img alt="内存地址V2" class="img-thumbnail" src="/assist/images/blog/761fcd40fb074a66915e845d08139997.png" /><br /> <br /> 3.在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11。<br /> <br /> <img alt="内存地址V3" class="img-thumbnail" src="/assist/images/blog/e247329a723f45c39dd4db9f50b59122.png" /><br /> 4.线程1开始提交更新,首先进行<strong>A和地址V的实际值比较(Compare)</strong>,发现A不等于V的实际值,提交失败。<br /> <img alt="内存地址V4" class="img-thumbnail" src="/assist/images/blog/ea99b78cf5fa414fa481ff39437f310e.png" /><br /> 5.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为<strong>自旋</strong>。<br /> <br /> <img alt="内存地址V5" class="img-thumbnail" src="/assist/images/blog/f03a795b87884530a02d4247f2e1bd26.png" /><br /> 6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行<strong>Compare</strong>,发现A和地址V的实际值是相等的。<br /> <br /> <img alt="内存地址6" class="img-thumbnail" src="/assist/images/blog/f1d8e07fac864ef9845aed9436d17c7f.png" /><br /> 7.线程1进行<strong>SWAP</strong>,把地址V的值替换为B,也就是12。<br /> <img alt="内存地址7" class="img-thumbnail" src="/assist/images/blog/c407d63f332043bc8c09f4582d66e6dc.png" /><br />     所以从思想上来说,Synchronized属于<strong>悲观锁</strong>,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于<strong>乐观锁</strong>,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。<br /> <br /> 两种机制CAS机制和Synchronized,没有绝对的好坏。那么如何抉择呢?<br /> 在并发量非常搞的情况下使用Synchronized同步锁更适合一些。<br /> <br /> <strong>CAS缺点:</strong> <p><strong>1.CPU开销较大</strong></p> <p>在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。</p> <p><strong>2.不能保证代码块的原子性</strong></p> <p>CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。</p> <p><strong>3.ABA问题</strong></p> <p>这是CAS机制最大的问题所在。</p> <p>什么是<strong>ABA</strong>问题?怎么解决?我们后面来详细介绍。</p>
  • Spring data-mongodb ID自增长注解实现

    Spring data-mongodb ID自增长注解实现,mongodb ID自增长<h2>1.创建一个文档。类似一个序列的作用</h2> <pre> <code class="language-java">package com.leftso.autoid; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; /** * 系统自增长表id存放表 * * @author xq * */ @Document(collection = "sys_sequence") public class SequenceId { @Id private String id; @Field("seq_id") private long seqId; @Field("coll_name") private String collName; public String getId() { return id; } public void setId(String id) { this.id = id; } public long getSeqId() { return seqId; } public void setSeqId(long seqId) { this.seqId = seqId; } public String getCollName() { return collName; } public void setCollName(String collName) { this.collName = collName; } } </code></pre> <h2>2.创建一个自定义注解用于处理需要自增长的ID</h2> <pre> <code class="language-java">package com.leftso.autoid; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义自增长ID注解 * * @author xq * */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) public @interface AutoValue { } </code></pre> <h2><br /> 3.重写新增的监听事件</h2> <span style="color:#27ae60"><strong>注意:各大网站可能会有相似的代码。以下部分和spring data mongodb版本有关。下面代码中重写的public void onBeforeConvert(final Object source)方法在1.8版本开始就废弃了,不过官方推荐: Please use onBeforeConvert(BeforeConvertEvent),以下则为1.8以后版本的使用方法</strong></span> <pre> <code class="language-java">package com.leftso.autoid; import java.lang.reflect.Field; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.FindAndModifyOptions; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; @Component public class SaveEventListener extends AbstractMongoEventListener<Object> { @Autowired private MongoTemplate mongo; @Override public void onBeforeSave(BeforeSaveEvent<Object> event) { Object source = event.getSource(); if (source != null) { ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() { public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { ReflectionUtils.makeAccessible(field); // 如果字段添加了我们自定义的AutoValue注解 if (field.isAnnotationPresent(AutoValue.class) && field.get(source) instanceof Number && field.getLong(source) == 0) { // field.get(source) instanceof Number && // field.getLong(source)==0 // 判断注解的字段是否为number类型且值是否等于0.如果大于0说明有ID不需要生成ID // 设置自增ID field.set(source, getNextId(source.getClass().getSimpleName())); } } }); } } /** * 获取下一个自增ID * * @param collName * 集合(这里用类名,就唯一性来说最好还是存放长类名)名称 * @return 序列值 */ private Long getNextId(String collName) { Query query = new Query(Criteria.where("coll_name").is(collName)); Update update = new Update(); update.inc("seq_id", 1); FindAndModifyOptions options = new FindAndModifyOptions(); options.upsert(true); options.returnNew(true); SequenceId seq = mongo.findAndModify(query, update, options, SequenceId.class); return seq.getSeqId(); } } </code></pre> <h2><br /> 4.使用自定义注解完成ID自增长的实现</h2> <pre> <code class="language-java">package com.leftso.entity; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import com.leftso.autoid.AutoValue; @Document public class Student { @AutoValue @Id private long id; private String name; public Student(String name) { super(); this.name = name; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } </code></pre> <br />  
  • 针对MyISAM高并发锁表的解决方案

    针对MyISAM高并发锁表的解决方案最近服务器上经常出现mysql进程占CPU100%的情况,使用命令 <pre> <code class="language-sql">show processlist</code></pre> 后,看到出现了很多状态为LOCKED的sql。使用 <pre> <code class="language-sql">show status like 'table%'</code></pre> 检查Table_locks_immediate和Table_locks_waited,发现Table_locks_waited偏 大。出问题的表是MyISAM,分析大概是MyISAM的锁表导致。<br /> MyISAM适合于读频率远大于写频率这一情况。而我目前的应用可能会出现在某一时段读写频率相当。大致如下:<br /> <br /> 一个客户端发出需要长时间运行的SELECT<br /> 其他客户端在同一个表上发出INSERT或者UPDATE,这个客户将等待SELECT完成<br /> 另一个客户在同一个表上发出另一个SELECT;因UPDATE或INSERT比SELECT有更高有优先级,该SELECT将等待UPDATE或INSERT完成,也将等待第一个SELECT完成<br /> <br /> 也就是说对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;<span style="color:#ff0000"><strong>MyISAM表的读操作与写操作之间,以及写操作之间是串行的!</strong></span><br /> 解决方案大概有如下几种:<br /> <br /> MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。 <ul> <li>0 不允许并发操作</li> <li>1 如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。</li> <li>2 无论MyISAM表中有没有空洞,都允许在表尾并发插入记录</li> </ul> 使用–low-priority-updates启用mysqld。这将给所有更新(修改)一个表的语句以比SELECT语句低的优先级。在这种情况下,在先前情形的最后的SELECT语句将在INSERT语句前执行。<br /> 为max_write_lock_count设置一个低值,使得在一定数量的WRITE锁定后,给出READ锁定<br /> 使用LOW_PRIORITY属性给于一个特定的INSERT,UPDATE或DELETE较低的优先级<br /> 使用HIGH_PRIORITY属性给于一个特定的SELECT<br /> 使用INSERT DELAYED语句<br /> <br /> 附加(一):<br /> mysql的myisam解决并发读写解决方法MyISAM在读操作占主导的情况下是很高效的。可一旦出现大量的读写并发,同InnoDB相比,MyISAM的效率就会直线下降,而且,MyISAM和InnoDB的数据存储方式也有显著不同:通常,在MyISAM里,新数据会被附加到数据文件的结尾,可如果时常做一些 UPDATE,DELETE操作之后,数据文件就不再是连续的,形象一点来说,就是数据文件里出现了很多洞洞,此时再插入新数据时,按缺省设置会先看这些洞洞的大小是否可以容纳下新数据,如果可以,则直接把新数据保存到洞洞里,反之,则把新数据保存到数据文件的结尾。之所以这样做是为了减少数据文件的大小,降低文件碎片的产生。但InnoDB里则不是这样,在InnoDB里,由于主键是cluster的,所以,数据文件始终是按照主键排序的,如果使用自增ID做主键,则新数据始终是位于数据文件的结尾。<br /> <br /> 了解了这些基础知识,下面说说MyISAM几个容易忽视的配置选项:<br /> concurrent_insert:<br /> 通常来说,在MyISAM里读写操作是串行的,但当对同一个表进行查询和插入操作时,为了降低锁竞争的频率,根据concurrent_insert的设置,MyISAM是可以并行处理查询和插入的: <ul> <li>当concurrent_insert=0时,不允许并发插入功能。</li> <li>当concurrent_insert=1时,允许对没有洞洞的表使用并发插入,新数据位于数据文件结尾(缺省)。</li> <li>当concurrent_insert=2时,不管表有没有洞洞,都允许在数据文件结尾并发插入。</li> </ul> <br /> 这样看来,把concurrent_insert设置为2是很划算的,至于由此产生的文件碎片,可以定期使用OPTIMIZE TABLE语法优化。<br /> max_write_lock_count:<br /> 缺省情况下,写操作的优先级要高于读操作的优先级,即便是先发送的读请求,后发送的写请求,此时也会优先处理写请求,然后再处理读请求。这就造成一个问题:一旦我发出若干个写请求,就会堵塞所有的读请求,直到写请求全都处理完,才有机会处理读请求。此时可以考虑使用>有了这样的设置,当系统处理一个写操作后,就会暂停写操作,给读操作执行的机会。<br /> low-priority-updates:<br /> 我们还可以更干脆点,直接降低写操作的优先级,给读操作更高的优先级。<br /> low-priority-updates=1<br /> 综合来看,concurrent_insert=2是绝对推荐的,至于max_write_lock_count=1和low-priority- updates=1,则视情况而定,如果可以降低写操作的优先级,则使用low-priority-updates=1,否则使用 max_write_lock_count=1。<br /> <br /> <br /> 附加(二):查看和修改concurrent_insert <pre> <code class="language-sql">show variables like 'concurrent_insert';</code></pre> <br /> <img alt="" class="img-thumbnail" src="/assist/images/blog/5a7f853bb5d049c98967aa1cf20cd329.png" /> <pre> <code class="language-sql">set global concurrent_insert=2; show variables like 'concurrent_insert';</code></pre> <img alt="" class="img-thumbnail" src="/assist/images/blog/24e87b2ca08b4e2d80af337af31a4138.png" /><br /> concurrent_insert:值  <table border="1" cellpadding="1" cellspacing="1" class="table table-bordered table-hover" style="width:500px"> <tbody> <tr> <td>值</td> <td>说明</td> </tr> <tr> <td>NEVER(或者0)</td> <td>不允许并发插入功能</td> </tr> <tr> <td>AUTO(或者1)</td> <td>(默认值)允许对没有洞洞的表使用并发插入,新数据位于数据文件结尾</td> </tr> <tr> <td>ALWAYS(或者2)</td> <td>不管表有没有洞洞,都允许在数据文件结尾并发插入</td> </tr> </tbody> </table> <br /> <span style="color:#ff0000">提示:MySQL中使用set设置的参数在MySQL服务重启后需要重新设定。如果需要永久生效配置MySQL配置文件my.ini/my.cnf</span>
  • Java编程之通过eclipse创建maven自定义项目原型模板(Archetype)

    Java编程之通过eclipse创建maven自定义项目原型模板(Archetype),Java编程,maven自定义项目模板<h2>一、简介</h2> <p>    Maven自定义原型有助于创建特定类型的项目结构,在任何标准的Maven原型中都是不可用的。在这个maven教程中,我们将学习从eclipse项目创建自定义原型。</p> <p>    它在某些场景中非常有用,特别是当我们需要在生成的maven项目中定制内容时,maven不提供这个选项。<br /> 本文将讲解以下内容:</p> <ul> <li>为什么需要定制原型?</li> <li>创建自定义原型</li> <li>导入Eclipse项目</li> <li>创建模板文件</li> <li>创建具有自定义原型的新项目</li> </ul> <h2>二、为什么需要定制原型?</h2>     一般来说,maven和一些第三方提供程序提供一些原型,这些原型在快速启动maven项目时非常有用。但是在我们的日常工作中,我们可能面临一些场景,我们需要为以下原因创建自定义的项目结构: <ul> <li>需要在组织内引入/实施特定的项目结构(包括包和骨架类)。</li> <li>在建立项目结构的过程中减少了大量的时间,通过快速地开始实际的工作来增加开发人员的生产力。</li> <li>减少代码审查工作,确保项目结构和预期的工件已经就位。</li> </ul> <h2>三、创建自定义原型</h2> Maven已经提供了一个创建原型的<code>maven-archetype-archetype去创建新的原型模板</code> <pre> <code>mvn archetype:generate -B -DarchetypeArtifactId=maven-archetype-archetype -DgroupId=com.howtodoinjava.archetype -DartifactId=maven-howtodoinjava-archetype -Dversion=1.0-SNAPSHOT</code></pre> 让我来理解上面的命令: <ul> <li><code>-DarchetypeArtifactId=maven-archetype-archetype</code>是maven提供的原型来创建新的自定义原型。</li> <li><code>-DgroupId=com.howtodoinjava.archetype</code> 是我们现在要创建的原型的组id。</li> <li><code>-DartifactId=maven-howtodoinjava-archetype</code> 是我们现在所创造的原型的名称。</li> <li><code>-Dversion=1.0-SNAPSHOT </code>是maven原型的版本。</li> </ul> <br /> 这里的所有参数都是不言自明的。我们仍然可以在任何时候遵循官方的maven文档了解更多细节。<br /> <br /> <em><strong>注意:<br /> 我们现在需要从命令提示符运行此命令,然后确保设置maven类路径。</strong></em><br /> 因此,当您从命令提示符运行这个命令时,您将在启动mvn命令的同一目录中生成一个maven项目。 <h2>四、将项目导入eclipse中</h2> 接下来的工作是在eclipse中导入这个项目,以进一步细化以满足我们的需求。在导入后,eclipse项目结构将是这样的。<br /> <img alt="项目结构" class="img-thumbnail" src="/assist/images/blog/ae2f47adecd5455facdb229592439928.jpg" /><br /> 一旦在eclipse中导入,我们需要执行以下步骤。 <ul> <li>删除的内容/ src / main /资源/ archetype-resources / src / main / java & / src / main /资源/ archetype-resources / src /测试/ java——主要是App.java和AppTest.java</li> <li>我们需要重新命名文件/ src / main / resources / meta - inf / maven /原型。archetype-metadata xml。xml,稍后我们需要更改这个文件的内容,我们稍后会看到。</li> </ul> 在上面的步骤之后,文件夹结构将会是这样,<br /> <img alt="项目结构2" class="img-thumbnail" src="/assist/images/blog/6775305f4d8040ee80fff9edaaa09e41.jpg" /> <h2>五、创建模板文件</h2> 现在,我们将为原型应该生成的类和资源创建一些模板文件。 <ul> <li>创建一个模板java文件\src\main\resources\archetype-resources\src\main\java\__project-name__.java和内容:    </li> </ul> <pre> <code class="language-java">package ${package}; public class ${project-name} { public static void ${project-name}SampleMethod() { System.out.println("Sample method generated by maven Archetype.."); } }</code></pre>     这个模板文件将根据运行时提供的占位符的值生成java文件。文件名中的位置必须被__NAME__包围,占位符逻辑名分隔符应该是连字符(-)。 <ul> <li>为属性文件创建一个模板;具有属性键值模板 <pre> <code>${project-name}.key=This is ${project-name} test property and the file name will be \src\main\resources\archetype-resources\src\main\resources\__property-file-name__.properties</code></pre> </li> </ul>     因为我们想在运行时生成文件名,所以我们在文件名中放置了一个占位符。 <ul> <li>您可以自由地按照您的需求来做模板文件。</li> <li>现在我们需要修改archetype-metadata.xml像下面:</li> </ul>     —requiredProperties部分将在生成这个原型的项目时声明所需的所有属性和它们的默认值。<br />     -文件集将声明将要生成的最终项目中的文件。<br />      <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <archetype-descriptor name="basic"> <requiredProperties> <requiredProperty key="project-name" /> <requiredProperty key="property-file-name"> <defaultValue>Resource-default</defaultValue> </requiredProperty> <!--JUnit version to use in generated project --> <requiredProperty key="junit-version"> <defaultValue>4.10</defaultValue> </requiredProperty> </requiredProperties> <!--Add new fileset for resources --> <!--Set filtered="true" to process files in that directory as templates --> <!--Set packaged="false" to remove package folder structure from resource directory --> <fileSets> <fileSet filtered="true"> <directory>src/main/resources</directory> <!--Filters example --> <includes> <include>*.txt</include> <include>*.properties</include> </includes> <excludes> <exclude>**/*.xml</exclude> </excludes> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/main/java</directory> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/test/java</directory> </fileSet> </fileSets> </archetype-descriptor></code></pre> <p>我们还需要更改pom.xml文件archetype-resources文件夹下接受运行时GAV(GroupId:ArtifactId:版本)坐标。我们需要将它更改为占位符,以接受运行时值,因为我们将使用这个原型生成项目。</p> <p><code>pom.xml<span style="font-family:sans-serif,Arial,Verdana,Trebuchet MS">在</span></code><code>archetype-resources下面应该是这样的:</code><br />  </p> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId> <version>${version}</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit-version}</version> <scope>test</scope> </dependency> </dependencies> </project></code></pre> 因此,我们主要完成了与原型更改相关的更改,现在我们应该通过命令mvn clean安装构建原型maven项目。项目应该构建良好,我们已经准备好使用这个原型来创建新的maven项目。 <h2>六、创建具有自定义原型的新项目</h2> <br /> 一旦我们成功地构建了原型项目,并且这个项目成功地安装在本地,我们将运行下面的命令来创建maven项目。 <pre> <code>mvn archetype:generate -DarchetypeCatalog=local -DarchetypeArtifactId=maven-howtodoinjava-archetype -DarchetypeGroupId=com.howtodoinjava.archetype -DarchetypeVersion=1.0-SNAPSHOT</code></pre> Maven将启动交互模式,并询问有关新Maven项目所需的所有属性。跳过默认值的属性,但是如果不确定最后一步中的配置,则可以重写默认值。<br /> <br /> 定制原型输入,例如:<br /> <img alt="原型" class="img-thumbnail" src="/assist/images/blog/6331413850e74436bae47a60a97c947c.jpg" /> <p>一旦这个步骤完成,我们就应该有一个基于我们开发的模板创建的新项目。</p> <p>因此,如果您在任何这样的场景中,默认maven原型是不够的,您可以使用maven的隐藏力量创建您自己的自定义原型。<br />  </p>
  • PowerDesigner教程 PowerDesigner设置MySQL自增长ID教程

    PowerDesigner配置MySQL数据库ID自增长教程PowerDesigner配置MySQL数据库ID自增长教程。PowerDesigner如何配置ID自增长?下面开始讲解1.确认当前选择的数据库(当然是选择MySQL啦)在PowerDesigner的菜单中选择database->Change Current DBMS...在打开的菜单中选择MySQL5.02.双击需要设置自增长ID的表头在打开的PowerDesigner表对话框中选择Cloumns这个tab页,也就是创建表字段的tab界面。点击Customize Cloumns and Filter,具体位置在哪里呢?看下面的图吧:​3.选中identity在步骤二弹出的PowerDesigner对话框中滚动下拉条找到identity并点击鼠标将其√上。如图:​勾上后点击ok按钮关闭对话框。4.Physical Options设置 在表的tab中找到Physical Options,点击切换该该tab界面。左边有一列的选择列表,找到auto_increment=(%d)点击选中并点击tab中间三个按钮中的 >>按钮将其放入右边的列表中。具体操作如图:​这里下面还可以设置自增长的启始值。5.设置字段的identity属性 表tab切换到columns tab,这时候会发现字段后面多了一个I的选择框,将鼠标放到I的顶部表头看到I代表的identity。这时候需要使用自增长的字段勾选I的选择框即可。如图:​6.PowerDesigner自增长sql预览 ​
  • spring boot整合spring security4自定义配置

    spring boot整合spring security4自定义配置spring boot整合spring security4自定义配置<br /> 1.项目结构图<br /> <img alt="项目结构图" class="img-thumbnail" src="/assist/images/blog/244477b7-3367-4c62-9dac-72c544104272.jpg" /><br /> 2.自定义spring拦截器UrlInterceptor <pre> <code class="language-java">package com.leftso.config; import org.springframework.web.servlet.HandlerInterceptor; /** * url 拦截器,实现该接口并在实现类上添加注解@Component,将会自动注入spring拦截器 * * @author leftso * */ public interface UrlInterceptor extends HandlerInterceptor { /*** * 需要拦截的url * * @return */ String[] pathPatterns(); /*** * 排除拦截 * * @return */ String[] excludePathPatterns(); int order(); } </code></pre> 3.获取spring context容器(为了后面动态添加拦截器) <pre> <code class="language-java">package com.leftso.config; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * * <pre> * [Summary] * 获取spring容器 * [Detail] * TODO * [Author] * leftso * [Version] * v1.0 * 2017年3月23日下午8:59:15 * </pre> */ @Component public class SpringUtils implements ApplicationContextAware { public static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringUtils.applicationContext == null) { SpringUtils.applicationContext = applicationContext; } } } </code></pre> 4.继承WebMvcConfigurerAdapter重写配置,动态添加拦截器 <pre> <code class="language-java">package com.leftso.config; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * 添加自定义的拦截器 * * @author * */ @Configuration public class MvcConfigurerAdapter extends WebMvcConfigurerAdapter { @Autowired SpringUtils springUtils; @Override public void addInterceptors(InterceptorRegistry registry) { super.addInterceptors(registry); @SuppressWarnings("static-access") Map<String, UrlInterceptor> urlInterceptors = springUtils.applicationContext .getBeansOfType(UrlInterceptor.class); if (!StringUtils.isEmpty(urlInterceptors)) { Set<String> keys = urlInterceptors.keySet(); Iterator<String> it = keys.iterator(); while (it.hasNext()) { String key = it.next(); UrlInterceptor urlInterceptor = urlInterceptors.get(key); String[] pathPatterns = urlInterceptor.pathPatterns(); if (pathPatterns == null) { pathPatterns = new String[] {}; } String[] excludePathPatterns = urlInterceptor.excludePathPatterns(); if (excludePathPatterns == null) { excludePathPatterns = new String[] {}; } registry.addInterceptor(urlInterceptor).addPathPatterns(pathPatterns) .excludePathPatterns(excludePathPatterns); } } } } </code></pre> 5.从这里开始,进行spring security相关配置,配置启用spring security <pre> <code class="language-java">package com.leftso.security.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll(); } /** * 需要拦截的url * * @return */ public String[] securityMethodUrlPatterns() { return new String[] { "/security/**" }; } } </code></pre> 注意:这里的<span style="color:#ff0000"><em><strong>http.authorizeRequests().anyRequest().permitAll();</strong></em></span>允许所有访问是为了后面自定义拦截器,不使用默认的spring filter<br /> 6.自定义一个spring security认证服务 <pre> <code class="language-java">package com.leftso.security.service; import javax.servlet.http.HttpServletRequest; /** * spring security 业务处理 * * @author LEFTSO * */ public interface SecurityService { /** * 获取认证信息 * * @param httpServletRequest */ public void authenticate(HttpServletRequest httpServletRequest); /** * 释放认证信息 */ public void release(); } </code></pre> <br /> 接口里面有两个方法,一个是通过请求信息生成认证信息,一个是释放认证信息资源<br /> 实现类: <pre> <code class="language-java">package com.leftso.security.service; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import com.leftso.security.UserAuthentication; @Component public class SecurityServiceimp implements SecurityService { @Override public void authenticate(HttpServletRequest httpServletRequest) { // 通过http请求cookie或者其他方式拿到用户登录后的凭证 String name = httpServletRequest.getParameter("name"); if ("leftso".equals(name)) { // 创建一个认证信息实例 // 1.创建当前用户的角色信息 List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); // 2.实例创建 Authentication authentication = new UserAuthentication(authorities, name); authentication.setAuthenticated(true); // 添加到安全容器 SecurityContextHolder.getContext().setAuthentication(authentication); }else{ SecurityContextHolder.clearContext(); } } @Override public void release() { SecurityContextHolder.clearContext(); } } </code></pre> <strong>这里的认证只是简单的写死了传递一个name参数且值为leftso才会认证成功并且添加一个ROLE_ADMIN的角色,当然实际应用这里应该去数据库查询认证,做好缓存等操作</strong><br /> 下面是一个上面用到的自定义认证实现: <pre> <code class="language-java">package com.leftso.security; import java.util.Collection; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; /** * 自定义用户认证 * * @author leftso * */ public class UserAuthentication extends AbstractAuthenticationToken { /** * */ private static final long serialVersionUID = 1L; private String name; public UserAuthentication(Collection<? extends GrantedAuthority> authorities) { super(authorities); } public UserAuthentication(Collection<? extends GrantedAuthority> authorities, String name) { super(authorities); this.name = name; } @Override public Object getCredentials() { // TODO Auto-generated method stub return null; } @Override public Object getPrincipal() { // TODO Auto-generated method stub return null; } @Override public String getName() { return name; } } </code></pre> <br /> 7.最重要的入口来了,自定义一个拦截器作为需要安全认证的入口 <pre> <code class="language-java">package com.leftso.security; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.ModelAndView; import com.leftso.config.UrlInterceptor; import com.leftso.security.config.WebSecurityConfig; import com.leftso.security.service.SecurityService; /** * spring security安全拦截器 * * @author leftso * */ @Component public class SecurityInterceptor implements UrlInterceptor { @Autowired WebSecurityConfig abstractWebSecurityConfig; @Autowired SecurityService securityService; @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3) throws Exception { // 后置释放认证信息 securityService.release(); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3) throws Exception { } @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("---Security:Interceptor--"); // 前置处理认证信息 securityService.authenticate(httpServletRequest); return true; } @Override public String[] pathPatterns() { // 配置中获取拦截信息 return abstractWebSecurityConfig.securityMethodUrlPatterns(); } @Override public int order() { return 0; } @Override public String[] excludePathPatterns() { // TODO Auto-generated method stub return null; } } </code></pre> <strong>拦截器前置调用获取用户信息生成认证信息<br /> 拦截器后置释放认证信息资源<br /> 注意前置必须返回TRUE,否则不能继续</strong><br /> <br /> 8.定义了两个简单的controller用于测试 <pre> <code class="language-java">package com.leftso.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexCtrl { @GetMapping("") public String index() { return "Welcome Index Page."; } @GetMapping("p1") public String p1() { return "P1"; } @GetMapping("login") public String login() { return "Login Page"; } } </code></pre> <pre> <code class="language-java">package com.leftso.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class SecurityCtrl { @PreAuthorize("hasRole('ROLE_ADMIN')") @GetMapping("/security/s1") public String s1() { return "s1"; } @GetMapping("/security/s2") public String s2() { return "s2"; } } </code></pre> <br /> 启动项目:<br /> <img alt="首页" class="img-thumbnail" src="/assist/images/blog/a6d224d8-4e39-42a0-b829-76c465bf8a2a.jpg" /><br /> 继续访问受保护的资源链接:<br /> <img alt="403" class="img-thumbnail" src="/assist/images/blog/d47b3c54-84cc-44cc-b485-351a9fcae658.jpg" /><br /> 由于没有权限,报错403禁止访问,(说明/security/**下面所有资源都会被拦截)<br /> <br /> 但是访问s2:<br /> <img alt="222" class="img-thumbnail" src="/assist/images/blog/8b6463bb-c29e-4b92-8ec4-131b7c28a263.jpg" /><br /> 不难发现可以访问s2,这是为啥呢?<br /> <img alt="区别" class="img-thumbnail" src="/assist/images/blog/6f8a187f-3d1d-4942-a400-4a515392f6fa.jpg" /><br /> <span style="color:#ff0000"><strong><em>需要保护的资源必须加上需要访问的权限角色</em></strong></span> <p><br /> 好了言归正传,如何访问到s1的资源呢?如下:<br /> <img alt="成功访问" class="img-thumbnail" src="/assist/images/blog/f6b7f2cd-94e7-4281-ae50-b6ea6721068e.jpg" /><br /> <strong>说明:实际应用中这个认证的信息是登录后的令牌并且是加密的,可能是放session中可能是cookie中</strong></p>