美文网首页
21.1并发(1)

21.1并发(1)

作者: 云木杉 | 来源:发表于2020-03-08 21:58 被阅读0次

并发编程

一直以为并发只是Java的一部分,没有想到这部分这么多,所以只能以系列的来记录,因为一篇的话根本描述不完,预计一个月的时间去了解实践。像书中讲的那样,你即使不了解它,你的程序也许也会正常的执行,偶尔出个问题,你也会觉得获取是其他地方的问题,久而久之并发的问题就不了了之了,但肯定会酝酿一个更大的bug来迎接你。

也是因为新环境的问题,认识到自己的不足,即使以前我也看过很多遍的,每次看的都比较煎熬,那么痛苦的环境下就放弃了,也是因为太急功近利,没有把心沉下来,好好的去探索里面的奥秘,像书中而言,就当学习一门新的语言了。

  • 顺序编程:即程序只能按照既定的顺序去执行。以前从来没有过这概念,但细想来确实,并发以前的程序
  • 并发的作用:并发解决的问题主要还是”速度“和”任务可管理“

并发的作用

  • 速度

    并发的初衷在于阻塞:当单核处理器时,并发程序其实开销会更大一些,因为还牵扯到线程(上下文)切换的操作,将程序作为单个的任务可能会更快一点,开销更少一点,但是 但是 但是 单个任务执行的时候,如果发生堵塞,那整个应用将停止运行,直至崩溃,并发处理的话却不会发生这种问题,一个任务停止的话,还有其他的任务可以执行,如果没有堵塞,在单核处理器上并发将没有意义;多核处理器的话,不适用并发的话,那会造成多大的浪费。

  • 改进代码设计

    在我看来,就是对代码结构的优化,文字看的不是太透彻,不对 是因为翻译的真的太懒了。

基本的线程机制

  • 任务

    1.Runnable是一个任务,是需要依附在线程上去启动的。实现Runnable需要重写一个run方法,如果这个任务没有依附在线程上的话,那么也就是个普通的方法,不具备任何的线程能力。

    2.Runnable的run方法是执行在新线程的,构造则是执行在原线程的(此处为main线程)。

public class LiftOff implements Runnable {

        // 成员变量 每个对象都有自己在内存中的一片区域,所以每个对象都有自己的成员
    protected int countDown = 10; // Default
    // 静态成员 成员不属于对象 存在于内存(静态储存区)中 
    private static int taskCount = 0;
    // 成员 final 对象只初始化一次
    private final int id = taskCount++;

    public LiftOff() {
    }

    public LiftOff(int countDown) {
        this.countDown = countDown;
    }

    public String status() {
        return "#" + id + "(" +
                (countDown > 0 ? countDown : "Liftoff!") + "), ";
    }

    public void run() {
        try {
            while (countDown-- > 0) {
                System.out.print(status());
                Thread.yield();
                TimeUnit.MILLISECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

       // LiftOff liftOff = new LiftOff();
       // 一个普通的run方法执行
       // liftOff.run(); 
       
       // LiftOff的run方法是执行在新线程的
       // 构造函数执行在新线程的开启线程(main线程)
       // Thread thread = new Thread(new LiftOff());
       // 开启一个线程 执行任务的run方法
       // thread.start(); 
       
    }

}
  • 线程池

    1.Executors.newCachedThreadPool() 这个模式,差不多就是有多少任务创建多少线程的意思

    2.Executors.newFixedThreadPool(3) 这个模式 传入对应的int参数 根据参数创建对应数量的线程池

    3.Executors.newSingleThreadExecutor() 创建单个线程池,如果提交多个任务,将会排队执行

    public static void main(String[] args) {
    
    //        ExecutorService executorService = Executors.newCachedThreadPool();
    //        ExecutorService executorService = Executors.newFixedThreadPool(3);
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 3; i++) {
                executorService.execute(new LiftOff());
            }
            executorService.shutdown();
        }
    
    
  • Callable

    有返回值的任务:实现Callable接口,重写call方法,线程池通过submit提交Callable获取Future数据,根据1.future.isDone()查看是否有结果

    2.future.get() 获取结果

    class TaskWithResult implements Callable<String> {
        private int id;
    
        public TaskWithResult(int id) {
            this.id = id;
        }
    
        public String call() {
            return "result of TaskWithResult " + id;
        }
    }
    
    public class CallableDemo {
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool();
            try {
                Future<String> submit = exec.submit(new TaskWithResult(9));
                String s = submit.get();
                System.out.println(s);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                exec.shutdown();
            }
        }
    }
    
  • Priority 优先级

    我在AS上测试了几下,感觉实用性不大

  • Thread.yield()

    让位机制,当前程序的主要工作完成以后,就可以让出CPU执行权限了,让位于其他线程,但线程里面的机制吧,你让位了也不一定丢失掉执行权限,你没让位不一定不丢失,所以不建议使用(官方建议,不依赖于此).

  • Daemon 后台线程

    后台线程:初始化线程后,可以通过setDaemon()将线程设置为后台线程(在start()开启之前);线程池通过Executors.newCachedThreadPool(new DaemonThreadFactory())设置(DaemonThreadFactory跟普通的唯一区别就是将Deaemon设置为了true,设置为了后台线程);

    1.后台线程是特别没有地位的线程,当非后台结束的时候,后台线程就连带着也终止了。

    2.Executors.newCachedThreadPool(new ThreadFactory())通过传入的参数创建新的线程。

    3.通过isDaemon判断是否为后台线程,而且在后台线程里创建的线程也是后台线程。

    public static void main(String[] args) throws Exception {
            ExecutorService exec = Executors.newCachedThreadPool(
                    new DaemonThreadFactory());
            for (int i = 0; i < 10; i++)
                exec.execute(new DaemonFromFactory());
            print("All daemons started");
            TimeUnit.MILLISECONDS.sleep(500); // Run for a while
    }
    
  • 关于线程的一些变体

    关于Thread和Runable的一些实现方式,推荐几种内部类的方式,但我觉得都还好,没有太惊艳的东西,可能是level太低,看不懂吧

    class InnerThread1 {
        private int countDown = 5;
        private Inner inner;
    
        private class Inner extends Thread {
            Inner(String name) {
                super(name);
                start();
            }
    
            public void run() {
                try {
                    while (true) {
                        print(this);
                        if (--countDown == 0) return;
                        sleep(10);
                    }
                } catch (InterruptedException e) {
                    print("interrupted");
                }
            }
    
            public String toString() {
                return getName() + ": " + countDown;
            }
        }
    
        public InnerThread1(String name) {
            inner = new Inner(name);
        }
    }
    
  • 加入一个线程

    标题是加入一个线程,其实我感觉叫“抢夺执行权”比较贴切,主要是Thread.join方法

    1. 当A线程在B线程里调用A.join方法时,B线程会先挂起,等A线程执行完再执行。
    2. 当A调用A.interrupt方法时,join方法将被中断执行,此刻执行A.interrupt的执行线程也会一同结束。(里面牵扯到interrupt标记位)
class Sleeper extends Thread {
    private int duration;

    public Sleeper(String name, int sleepTime) {
        super(name);
        duration = sleepTime;
        start();
    }

    public void run() {
        try {
            sleep(duration);
        } catch (InterruptedException e) {
            print(getName() + " was interrupted. " +
                    "isInterrupted(): " + isInterrupted());
            return;
        }
        print(getName() + " has awakened");
    }
}

class Joiner extends Thread {
    private Sleeper sleeper;

    public Joiner(String name, Sleeper sleeper) {
        super(name);
        this.sleeper = sleeper;
        start();
    }

    public void run() {
        try {
            sleeper.join();
        } catch (InterruptedException e) {
            print("Interrupted");
        }
        print(getName() + " join completed");
    }
}

public class Joining {
    public static void main(String[] args) {
        Sleeper sleepy = new Sleeper("Sleepy", 1500);
        Sleeper grumpy = new Sleeper("Grumpy", 1500);
        Joiner dopey = new Joiner("Dopey", sleepy);
        Joiner doc = new Joiner("Doc", grumpy);
        grumpy.interrupt();
    }
}
  • 捕获异常

    说是捕捉线程中逃跑的异常,但我感觉大概就是运行时异常吧

    可以通过一个静态方法setDefaultUncaughtExceptionHandler配置一个异常处理器,

class ExceptionThread2 implements Runnable {
   public void run() {
       throw new NullPointerException();
   }
}

class MyUncaughtExceptionHandler implements
       Thread.UncaughtExceptionHandler {
   public void uncaughtException(Thread t, Throwable e) {
       System.out.println("caught " + e);
   }
}

class HandlerThreadFactory implements ThreadFactory {
   public Thread newThread(Runnable r) {
       Thread t = new Thread(r);
       t.setUncaughtExceptionHandler(
               new MyUncaughtExceptionHandler());
       return t;
   }
}

public class CaptureUncaughtException {
   public static void main(String[] args) {
       ExecutorService exec = Executors.newCachedThreadPool(
               new HandlerThreadFactory());
       exec.execute(new ExceptionThread2());
   }
}

也可以通过自定义异常处理器处理

public class CaptureUncaughtException {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool(
                new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
    }
}

class HandlerThreadFactory implements ThreadFactory {
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setUncaughtExceptionHandler(
                new MyUncaughtExceptionHandler());
        return t;
    }
}

class MyUncaughtExceptionHandler implements
      Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
  System.out.println("caught " + e);
}
}

class ExceptionThread2 implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
}

还有最简单的一种方式,在不存在专有未捕获异常处理器时调用

public class SettingDefaultHandler {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(
                new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}

class MyUncaughtExceptionHandler implements
        Thread.UncaughtExceptionHandler {
  public void uncaughtException(Thread t, Throwable e) {
    System.out.println("caught " + e);
  }
}

public class ExceptionThread implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
}

相关文章

网友评论

      本文标题:21.1并发(1)

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