高并发多线程知识点

1.并发线程的3个特性

(1)原子性问题:如果多个线程共用共享资源,那么会出现资源被修改,但是其他线程拿不到最新的值,这时候可以用volatile(对其他线程可见和防止指令重排序,但是不能保证原子性,没办法实现线程同步)解决

(2)可见性问题:有些变量比如 i++,就不能用volatile,只能用同步锁  synchronized(可以解决原子性,有序性,可见性,但是不适用高并发,影响性能),单个线程自己独享。

(3)有序性问题:用JMM规范去解决多线程顺序的问题

2.java可以用内存屏障来解决编译器重排序的问题

3.volatile应用场景

   线程执行run()的时候我们需要在线程中不停的做一些事情,比如while循环,那么这时候该如何停止线程呢?如果线程做的事情不是耗时的,那么只需要使用一个标志即可。如果需要退出时,调用setStop()即可。这里就使用了关键字volatile,这个关键字的目的是如果修改了isStop的值,那么在while循环中可以立即读取到修改后的值。

这里如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了。

4.解决线程安全问题

(1)方法一:

import com.ruoyi.framework.web.domain.server.Sys;
import net.sf.ehcache.concurrent.Sync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Thread1 implements Runnable {

   private int ticket = 5;
   Lock lock = new ReentrantLock();

   @Override
   public void run(){

       while (ticket>0) {
          synchronized (this) {
            if(ticket>0){
               ticket--;
               System.out.print(Thread.currentThread().getName() + "窗口卖了1张票还有剩余票:" + ticket);
               System.out.print("\n");
            }

          }
       }
   }

}

(2)方法二:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Thread1 implements Runnable {

   private int ticket = 5;
   Lock lock = new ReentrantLock();

   @Override
   public void run(){

       while (ticket>0) {
          lock.lock();//多个线程竞争一个锁
         try{
            if(ticket>0){
               ticket--;
               System.out.print(Thread.currentThread().getName() + "窗口卖了1张票还有剩余票:" + ticket);
               System.out.print("\n");
            }
         }catch (Exception e){

         }finally {
            lock.unlock();//抢到的线程执行完业务释放锁
         }
         try {
            Thread.sleep(1000);
         }catch (Exception e){

         }

       }
   }

}

  思考:为什么lock比synchronized 好用,因为finally 肯定会执行,那么就会解决死锁的问题

6.JVM中的锁解决不了分布式高并发多任务进程,得用分布式锁,三种分布式锁:Mysql数据库的锁机制实现,要求数据库支持行级锁;Redis的setnx命令实现;利用zookpper的节点特性。

 

 

下面重点讲解redis分布式

1.pom文件

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

2.配置文件

# redis配置,默认没有密码
redis:
  host: 192.168.1.142
  port: 6379
  password: 123456
  jedis:
    pool:
      min-idle: 8
      max-idle: 500
      max-active: 2000
      max-wait: 10000
  timeout: 0

3.代码实现

Jedis jedis  =  new Jedis("192.168.1.142:6379");

String ret = jedis.set("wds","123456","NX","PX",10000);

注意:当redis中存在key,则不会有任何操作,当没有相同的key,才会往里面插数据;

           NX表示使用"nx"模式,"PX"表示失效的时间单位毫秒;

            加锁通过setnx写值成功则加锁成功;

            解锁:匹配随机值,删除redis上的特点key数据,要保证获取数据,判断一致以及删除

                       数据三个操作是原子的,执行如下lua脚本:

                             if redis.call("get",KEYS[1])==ARGV[1] then

                                      return redis.call("del",KEYS[1])

                               else

                                         return 0

                                 end

4.lua脚本,因为redis是单进程单线程模式,所以一开始就会执行lua脚本。redisTemplate不可以加锁的原因是它不支持setnx方法。

5.具体的加锁解锁代码(参考:https://www.cnblogs.com/linjiqin/p/8003838.html

加锁:

public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

解锁:

 private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值