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来记录线程中各个方位调用耗时的帮助类。

Java并发编程笔记—基础知识—实用案例
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
Java并发编程笔记—基础知识—实用案例
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");

    }

}
待补充……
 
 

你可能感兴趣的:(Java并发编程)