美文网首页Android知识
java中Thread的深入了解(三)

java中Thread的深入了解(三)

作者: Charon_Pluto | 来源:发表于2017-09-08 14:50 被阅读14次

    1.volatile

    可以强制去掉缓存区 每次都去从内存读值

        static byte value=0;
        static boolean finish=false;
        public static void testVolatile() throws InterruptedException {
            value=0;
            finish=false;
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(value==0&&!finish);
                    console.info
    
                            ("value is "+value+" finish is "+finish);
    
                }
            }).start();
            Thread.sleep(1000);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    value=10;
                    finish=true;
                    console.info
    
                            ("has set the finish");
                }
            }).start();
        }
    

    可以看到运行结果如图:第一个线程并没有如期结束,在线程循环里, 如果没有与其他线程产生交互 ,他不会读取内存的值 会始终使用缓存的值,所以第一个线程的死循环。

    结果

    这是因为cpu和线程有个缓存区,当线程运行的时候 读入需要的变量进入缓冲区

    线程运行

    所以我们可以通过volatile关键字 保证每次读取都会从实际内存里读取

    volatile字段添加位置

    2.concurrent的BlockingQueue

    java 1.6之后有的这个接口,这个是一个线程安全的对象,用于线程之间的通信。
    BlockingQueue是一个线程安全的队列接口 ,提供了 put take poll等方法
    实现类有 ArrayBlockingQueue LinkedBlockingQueue PriorityBlockingQueue。

    • ArrayBlockingQueue初始化的时候要指定大小,且容量就是初始化指定的
    • LinkedBlockingQueue 是链表实现 不需要指定大小 会自动扩容
    • PriorityBlockingQueue 是一个有优先级的队列,可以让优先级高的先处理,优先级低的后处理

    生产者 消费者 用这个就很好解决,如图:

    生产者和消费者 BlockingQueue中线程运行

    2.1LinkedBlockingQueue

    public static void blockingQueueTest(){
            BlockingQueue<String> blockingDeque=new LinkedBlockingQueue<String>();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        console.info
    
                                (blockingDeque.take());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"消费者").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        blockingDeque.put("new Msg");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"生产者").start();
        }
    

    2.2PriorityBlockingQueue (优先级)

     public static class Message implements Comparable<Message>{
    
            public  String name;
            public int value=0;
    
            public Message(int value,String name) {
                this.value = value;
                this.name=name;
            }
    
            @Override
            public int compareTo(Message o) {
                return value-o.value;
            }
        }
    
        public static void blockingQueueTests(){
            PriorityBlockingQueue<Message> blockingDeque=new PriorityBlockingQueue <Message>();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<100;i++) {
                        try {
                            console.info
    
                                    (blockingDeque.take().name);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },"消费者").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    blockingDeque.put(new Message(100,"zhangsan"));
                    blockingDeque.put(new Message(0,"lisi"));
                    blockingDeque.put(new Message(0,"wangwu"));
                    blockingDeque.put(new Message(100,"zhaosi"));
                }
            },"生产者").start();
        }
    

    可以看put方法是阻塞的 直到里面有足够的空间存储,take也是阻塞的直到有内容的时候返回,所以上面的线程 即是是消费者直接运行 生产者后运行 也能正确的指向。

    3.concurren包的线程池

    Executors.newScheduledThreadPool 创建固定大小的线程池
    Executors.newFixedThreadPool 创建固定大小的线程池
    Executors.newCachedThreadPool 创建缓存线程池 从0 开始 每个线程存货一分钟
    Executors.newSingleThreadExecutor 创建只有一个线程的线程池

    实际上都是创建了一个ThreadPoolExecutor 控制了不同参数

    /**
      *
      *  corePoolSize 线程池最小保持线程数 即是他们处于空闲状态
      *  maximumPoolSize 线程池最大线程数
      *  keepAliveTime 如果线程数超出最小保持 那么线程最大空闲存活实际
      *  unit 存活时间单位单位
      *  初始工作的缓冲区
      */
     public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
                                  }
    

    eg:

     public static void CountDownLatchTest() throws InterruptedException {
            long startTime=System.currentTimeMillis();
            CountDownLatch countDownLatch=new CountDownLatch(65535);
            ScheduledExecutorService service = Executors.newScheduledThreadPool(9000);
            for(int i=0;i<65535;i++){
                service.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(200);
                            countDownLatch.countDown();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
            try {
                countDownLatch.await();
                service.shutdown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("所有线程完成了!"+(System.currentTimeMillis()-startTime)+"ms");
        }
    

    4.ReentrantReadWriteLock

    synchronized 获取锁的方式 是不区分读写的,只要获取锁就排斥其他所有线程
    ReentrantReadWriteLock 把锁分成读锁和写锁:读锁不排斥读锁 也就是多个线程都可以进入读锁。
    写锁 排斥读锁和其它写锁,换成写锁的话只有一个线程能获取到锁。
    锁用完需要进行释放unlock()

      public static void ReadWriteLock(){
            ReadWriteLock reentrantLock=new ReentrantReadWriteLock();
            Lock readLock=reentrantLock.readLock();
            Lock writeLock=reentrantLock.writeLock();
            for(int i=0;i<2;i++)
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        readLock.lock();
                        console.info
    
                                ("获取到了读锁1");
                        try {
                            Thread.sleep(20);
                            readLock.unlock();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            for(int i=0;i<2;i++)
            new Thread(new Runnable() {
                @Override
                public void run() {
                    writeLock.lock();
                    console.info
    
                            ("获取到了写锁2");
                    try {
                        Thread.sleep(2000);
                        writeLock.unlock();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
        }
    

    会发现不是立马两个线程都运行了,是先运行了第一个,等待线程运行完成,在运行第二个。

    运行结果

    相关文章

      网友评论

        本文标题:java中Thread的深入了解(三)

        本文链接:https://www.haomeiwen.com/subject/pwdvjxtx.html