zoukankan      html  css  js  c++  java
  • 线程锁(转)

    使用线程锁的场合

    程序中经常采用多线程处理,这可以充分利用系统资源,缩短程序响应时间,改善用户体验;如果程序中只使用单线程,那么程序的速度和响应无疑会大打折扣。
    但是,程序采用了多线程后,你就必须认真考虑线程调度的问题,如果调度不当,要么造成程序出错,要么造成荒谬的结果。

    一个讽刺僵化体制的笑话

    前苏联某官员去视察植树造林的情况,现场他看到一个人在远处挖坑,其后不远另一个人在把刚挖出的坑逐个填上,官员很费解于是询问陪同人员,当地管理人员说“负责种树的人今天病了”。
    上面这个笑话如果发生在程序中就是线程调度的问题,种树这个任务有三个线程:挖坑线程,种树线程和填坑线程,后面的线程必须等前一个线程完成才能进行,而不是按时间顺序来进行,否则一旦一个线程出错就会出现上面荒谬的结果。

    用线程锁来处理两个线程先后执行的情况

    在程序中,和种树一样,很多任务也必须以确定的先后秩序执行,对于两个线程必须以先后秩序执行的情况,我们可以用线程锁来处理。
    线程锁的大致思想是:如果线程A和线程B会执行实例的两个函数a和b,如果A必须在B之前运行,那么可以在B进入b函数时让B进入wait set,直到A执行完a函数再把B从wait set中激活。这样就保证了B必定在A之后运行,无论在之前它们的时间先后顺序是怎样的。

    线程锁的代码

    如右,SwingComponentLock的实例就是一个线程锁,lock函数用于锁定线程,当完成状态isCompleted为false时进入的线程会进入SwingComponentLock的实例的wait set,已完成则不会;要激活SwingComponentLock的实例的wait set中等待的线程需要执行unlock函数。

    public class SwingComponentLock {
    // 是否初始化完毕
    boolean isCompleted = false;

    /**
    * 锁定线程
    */
    public synchronized void lock() {
    while (!isCompleted) {
    try {
    wait();
    } catch (Exception e) {
    e.printStackTrace();
    logger.error(e.getMessage());
    }
    }
    }

    /**
    * 解锁线程
    *
    */
    public synchronized void unlock() {
    isCompleted = true;
    notifyAll();
    }
    }

    线程锁的使用

    public class TreeViewPanel extends BasePanel {
    // 表空间和表树
    private JTree tree;

    // 这个是防树还未初始化好就被刷新用的
    private SwingComponentLock treeLock;

    protected void setupComponents() {
    // 初始化锁
    treeLock = new SwingComponentLock();

    // 创建根节点
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("DB");
    tree = new JTree(root);

    // 设置布局并装入树
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    add(new JScrollPane(tree));

    // 设置树节点的图标
    setupTreeNodeIcons();

    // 解除对树的锁定
    treeLock.unlock();
    }

    /**
    * 刷新树视图
    *
    * @param schemas
    */
    public synchronized void refreshTree(List<SchemaTable> schemas) {
    treeLock.lock();

    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

    DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
    root.removeAllChildren();

    for (SchemaTable schemaTable : schemas) {
    DefaultMutableTreeNode schemaNode = new DefaultMutableTreeNode(
    schemaTable.getSchema());

    for (String table : schemaTable.getTables()) {
    schemaNode.add(new DefaultMutableTreeNode(table));
    }

    root.add(schemaNode);
    }

    model.reload();
    }

    讲解

    上页中,setupComponents函数是Swing主线程执行的,而refreshTree函数是另外的线程执行(初始化时程序开辟一个线程执行,其后执行由用户操作决定)。 refreshTree函数必须要等setupComponents函数把tree初始化完毕后才能执行,而tree初始化的时间较长,可能在初始化的过程中执行refreshTree的线程就进入了,这就会造成问题。
    程序使用了一个SwingComponentLock来解决这个问题,setupComponents一开始就创建SwingComponentLock的实例treeLock,然后执行refreshTree的线程以来就会进入treeLock的wait set,变成等待状态,不会往下执行,这是不管tree是否初始化完毕都不会出错;而setupComponents执行到底部会激活treeLock的wait set中等待的线程,这时再执行refreshTree剩下的代码就不会有任何问题,因为setupComponents执行完毕tree已经初始化好了。
    让线程等待和激活线程的代码都在SwingComponentLock类中,这样的封装对复用很有好处,如果其它复杂组件如table也要依此办理直接创建SwingComponentLock类的实例就可以了。如果把wait和notifyAll写在TreeViewPanel类中就不会这样方便了。

    总结

    线程锁用于必须以固定顺序执行的多个线程的调度。
    线程锁的思想是先锁定后序线程,然后让线序线程完成任务再解除对后序线程的锁定。
    线程锁的写法和使用一定要理解记忆下来。

  • 相关阅读:
    自定义动画animate()
    【Java】正则表达式
    【Java】连接数据库SQLServer
    【Java】导入导出TXT文件
    【数据库】SELECT语句
    数据结构与算法系列之目录
    【Java】员工统计
    【Java】生产者消费者模式
    【Java】购物超市
    【Java】导入导出Excel表格
  • 原文地址:https://www.cnblogs.com/qingzhibin/p/3635366.html
Copyright © 2011-2022 走看看