zoukankan      html  css  js  c++  java
  • Java并发编程笔记—基础知识—实用案例

    如何正确停止一个线程

    1)共享变量的使用

    中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。当线程等待某些事件发生而被阻塞,又会发生什么?果线程被阻塞,它便不能核查共享变量,也就不能停止。这在许多情况下会发生,例如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()时。
     

    2)Thread.interrupt()的理解

    Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
     

    正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。 

    实例如下:

    package com.iflytek.ossp.bliserver.common.utils;
    
    public abstract class MyWorkRunnable implements Runnable {
        volatile Thread mTheThread = null;
    
        @Override
        public void run() {
            if (mTheThread != Thread.currentThread()) {
                throw new RuntimeException();
            }
    
            while (!Thread.interrupted() && mTheThread != null) {
                execute();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    mTheThread.interrupt();
                }
            }
        }
    
        public void start() {
            mTheThread = new Thread(this);
            mTheThread.start();
        }
    
        public void stop() {
            if (mTheThread != null) {
                mTheThread.interrupt();
                try {
                    mTheThread.join(); // 等待线程彻底结束
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    mTheThread.interrupt();
                }
            }
        }
    
        public abstract void execute();
    }

      

    在现有的线程安全类中添加功能

    假设有一个线性的链表,需要提供一个原子的“若没有则添加”的操作。

    第一种方法:扩展现有的类

    public class BetterVector<E> extends Vector<E> {
    
        public synchronized boolean putIfAbsent(E x) {
            boolean absent = !contains(x);
            if (absent)
                add(x);
            return absent;
    
        }
    }

    第二种方法:客户端加锁

    注意下面这张方法是不能实现线程安全的。

    public class ListHelper<E> {
    
        public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    
        public synchronized boolean putIfAbsent(E x) {
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    
    }

    问题出在使用错误的锁进行同步,无论List使用哪一个锁来保护它的状态,可以确定的是这个锁并不是ListHelper上的锁。

    正确的方式是是使用客户端加锁:

    public class ListHelper<E> {
    
        public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    
        public boolean putIfAbsent(E x) {
            synchronized (list) {
                boolean absent = !list.contains(x);
                if (absent)
                    list.add(x);
                return absent;
            }
        }
    }

    但这张方法是脆弱的,因为需要依赖List的加锁策略,只有在遵循加锁策略上的类使用客户端加锁才有效。

    最好的方式是使用组合:

    public class ImprovedList<T> implements List<T> {
    
        private final List<T> list;
    
        public ImprovedList(List<T> list) {
            this.list = list;
        }
    
        public synchronized boolean putIfAbsent(T x) {
            boolean contains = !list.contains(x);
            if (contains)
                list.add(x);
            return contains;
        }
    
        public synchronized void clear() {
            list.clear();
        }
    
        // ......按照类似的方式委托list的其他方法。
    }

      

    ThreadLocal的使用

    下面是一个使用ThreadLocal来记录线程中各个方位调用耗时的帮助类。

    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 用于跟踪各个线程中方法调用的耗时等信息。
     * <p>
     * 对于一个线程串行多次执行同一个方法,只能跟踪到最后一次的执行信息。
     * 
     * @author jdzhan,2013-1-13
     * 
     */
    public final class MethodCallTrace {
    
        private static final ThreadLocal<Map<String, TraceInfo>> mutilThreadTraces = new ThreadLocal<Map<String, TraceInfo>>() {
            @Override
            protected Map<String, TraceInfo> initialValue() {
                return new HashMap<String, TraceInfo>();
            }
        };
    
        /**
         * 开始跟踪
         */
        public static void start() {
            String funcName = new Throwable().getStackTrace()[1].getMethodName();
            start(funcName);
        }
    
        /**
         * 结束跟踪
         */
        public static void end() {
            String funcName = new Throwable().getStackTrace()[1].getMethodName();
            end(funcName);
        }
    
        /**
         * 开始跟踪
         * 
         * @param funcName
         *            指定一个方法名称
         */
        public static void start(String funcName) {
    
            TraceInfo item = new TraceInfo();
            item.setStartTime(new Date());
    
            Map<String, TraceInfo> traces = null;
            if (mutilThreadTraces.get() == null) {
                traces = new HashMap<String, TraceInfo>();
                mutilThreadTraces.set(traces);
            } else {
                traces = mutilThreadTraces.get();
            }
    
            traces.put(funcName, item);
        }
    
        /**
         * 结束跟踪
         * 
         * @param funcName
         *            指定一个方法名
         */
        public static void end(String funcName) {
            Map<String, TraceInfo> traces = mutilThreadTraces.get();
            if (traces == null) {
                return;
            }
            TraceInfo item = traces.get(funcName);
            if (item == null) {
                return;
            }
            item.setEndTime(new Date());
        }
    
        public static Map<String, TraceInfo> getTrace() {
            return mutilThreadTraces.get();
        }
    }
    MethodCallTrace
    import java.util.Date;
    
    public class TraceInfo {
    
        private Date startTime;
        private Date endTime;
    
        public Date getStartTime() {
            return new Date(startTime.getTime());
        }
    
        public void setStartTime(Date startTime) {
            this.startTime = new Date(startTime.getTime());
        }
    
        public Date getEndTime() {
            return new Date(endTime.getTime());
        }
    
        public void setEndTime(Date endTime) {
            this.endTime = new Date(endTime.getTime());
        }
    
        /**
         * 获取方法调用的耗时,如果返回-1或小于0说明记录存在问题。
         * 
         * @return
         */
        public long getCallTime() {
            if (startTime == null || endTime == null) {
                return -1;
            }
            return endTime.getTime() - startTime.getTime();
        }
    }
    TraceInfo
    import java.util.Map;
    import java.util.Map.Entry;
    
    public class ThreadLocalTest {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            MethodCallTrace.start();
            for (int i = 0; i < 3; i++) {
                new Thread(new Runnable() {
    
                    @Override
                    public void run() {
                        test();
                        test2();
                        test3();
                        Map<String, TraceInfo> trace = MethodCallTrace.getTrace();
    
                        for (Entry<String, TraceInfo> entry : trace.entrySet()) {
                            System.out.println(Thread.currentThread().getId() + entry.getKey() + ":" + entry.getValue().getCallTime() + "毫秒");
                        }
                    }
                }).start();
            }
            
            while (true) {
                try {
                    Thread.sleep(3000);
                    MethodCallTrace.end();
                    Map<String, TraceInfo> trace = MethodCallTrace.getTrace();
                    for (Entry<String, TraceInfo> entry : trace.entrySet()) {
                        System.out.println("主线程:" + entry.getKey() + ":" + entry.getValue().getCallTime() + "毫秒");
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
        private static void test() {
            MethodCallTrace.start();
            for (int i = 0; i < 1; i++) {
                try {
                    Thread.sleep(Thread.currentThread().getId());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            MethodCallTrace.end();
        }
    
        private static void test2() {
            MethodCallTrace.start("test2");
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(Thread.currentThread().getId());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            MethodCallTrace.end("test2");
        }
    
        private static void test3() {
            MethodCallTrace.start("test3");
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(Thread.currentThread().getId());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            MethodCallTrace.end("test3");
        }
    }
    待补充……
     
     
  • 相关阅读:
    logistics regression
    dir 以及 attrib
    python 爬取有道翻译
    spss 逐步回归
    JQuery传值给.ashx乱码
    字符串转换成json的三种方式
    启动数据库SQL Server Service Broker
    ASP.NET两种缓存方式
    VS安装控件后报错
    sql server中有不是全数字的字符串
  • 原文地址:https://www.cnblogs.com/zhanjindong/p/3515234.html
Copyright © 2011-2022 走看看