Java编程思想(十七) —— 并发之基本概念和方法

来源:互联网 发布:网络销售计划 编辑:程序博客网 时间:2024/06/07 05:19

先提前写并发这一块,前面的数组,IO,泛型,注解是知识点很多很小的东西,放到后面写。


确实如书上所说,学到这里都是顺序编程,程序所有事物任意时刻只能执行一个步骤。这远远不够,想想双11淘宝技术如果并发不牛叉的话根本处理不了这么 庞大的数据量。——《淘宝技术这十年》。技术的积累真的是很深。


作者介绍的时候最后很有趣说,学会这章之后很容易变得过分自信,如果要学好,还需要继续深入。


1)基本的线程机制

进程有3种状态,运行,就绪,阻塞。

线程有4种状态,运行,就绪,阻塞和终止。网上还有其他分法。

所谓进程和线程的联系区别,是经常问的问题,回答倒是难答。

简单说的话,一个程序至少有一个进程,而一个进程里面至少有一个线程,线程间共享进程内存空间,而进程与进程间独立。


一、线程驱动任务:

class MyThread implements Runnable{    //Runnable 源码    /*public    interface Runnable {        public abstract void run();    }*/    int number = 10;    private static int count = 0;    //之前用过,就是希望id初始化之后不再改变    private final int id = count++;    public MyThread(){}    public MyThread(int number){        number = number;    }    public String status(){        return id+" "+ (number > 0? number :"zero");    }    public void run() {        while(number-->0){            System.out.println(status());            Thread.yield();        }    }}public class TestThread {    public static void main(String[] args) {        MyThread m = new MyThread();        m.run();    }}

Thread.yield()是线程调度器,其实就是说我跑过了,我把cpu让给你。

一run,马上就跑了起来。


二、用Thread驱动:

Thread(Runnable target)

Allocates a new Thread object.

public class TestThread {    public static void main(String[] args) {        Thread t = new Thread(new MyThread());        t.start();        System.out.println("late");    }}

start会为线程先做初始化操作,然后调用run方法。此时的run方法是其他线程调用,main线程继续执行,所以先打印出late。当然,如果改成t.run(),那么late就会最后打印了。


Tread的run和start有什么区别呢——java中thread的start()和run()的区别 。

按照里面的英文说法,假如我们再:

public static void main(String[] args) {        Thread t = new Thread(new MyThread());        Thread t1 = new Thread(new MyThread());        t.start();        t1.start();        // t.run();        // t1.run();        System.out.println("late");    }    result:    start方法:    1 9    0 9    1 8    0 8    1 7    0 7    1 6    0 6    1 5    0 5    1 4    0 4    1 3    0 3    1 2    0 2    1 1    0 1    1 zero    0 zero    run方法:    0 9    0 8    0 7    0 6    0 5    0 4    0 3    0 2    0 1    0 zero    1 9    1 8    1 7    1 6    1 5    1 4    1 3    1 2    1 1    1 zero

run和start是不同的,两个线程使用start方法的时候,两个是同时运行的,结果是交错的。

两个线程使用run方法的时候,运行是顺序的,哪个在前,先运行哪个。所以看到的是t的输出先。

start方法会让线程运行是异步的(asynchronously),在启动的时候,其实start调用的是MyThread的run方法,但是此时执行的是其他线程,而执行main方法的线程继续,所以先打印了df,其余两个线程在交错运行中。

run方法是同步的(synchronously),只有等它运行完才进行下一步的操作,所以df在最后打印。

2)用Executor管理Thread

java.util.concurrentInterface Executor

An object that executes submitted Runnable tasks.  An Executor is normally used instead of explicitly creating threads. For example, rather than invokingnew Thread(new(RunnableTask())).start() for each of a set of tasks, you might use:

 Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2()); ...

一个执行提交的Runnable任务的类。代替显式地创建线程。

    ExecutorService es = Executors.newCachedThreadPool();    es.execute(new MyThread());    es.execute(new MyThread());    es.shutdown();  //es.execute(new MyThread()); shutdown之后不能提交新任务

newCachedThreadPool为每个任务都创建一个线程,

ExecutorService es = Executors.newFixedThreadPool(2);es.execute(new MyThread());
newFixedThreadPool(int n)不会为每个任务都创建一个线程,而是限制了线程,需要线程的事件会到这个池里拿到线程。

newSingleThreadExecutor()的作用就和FixedThreadPool中n的值为1一样。

ExecutorService ess = Executors.newFixedThreadPool(1);ess.execute(new MyThread());ess.execute(new MyThread());ess.execute(new MyThread());ess.execute(new MyThread());ess.execute(new MyThread());0 90 80 70 60 50 40 30 20 10 zero1 91 81 71 61 51 41 31 21 11 zero2 92 82 72 62 52 42 32 22 12 zero3 93 83 73 63 53 43 33 23 13 zero4 94 84 74 64 54 44 34 24 14 zero

3)从任务中返回值

Runnable虽然执行自己的工作,但是不返回值,实现Callable接口可以返回值。

java.util.concurrent
Interface Callable<V>

Type Parameters:
V - the result type of method call

Modifier and TypeMethod and DescriptionVcall()
Computes a result, or throws an exception if unable to do so.

class MyCall implements Callable<String>{    private int id;    public MyCall(int id){        this.id = id;    }    public String call() throws Exception {        return "call"+id;    }}public class ExecuteCall {    public static void main(String[] args) {        ExecutorService es = Executors.newCachedThreadPool();        ArrayList<Future<String>> a = new ArrayList<Future<String>>();        for (int i = 0; i < 5; i++) {            a.add( es.submit(new MyCall(i)));        }        for(Future f : a){            try {                System.out.println(f.get());            } catch (InterruptedException e) {                e.printStackTrace();            } catch (ExecutionException e) {                e.printStackTrace();            }        }    }}

这个例子很多新东西,因为我们是要返回值,所以用了MyCall实现了Call接口,并且必须使用ExecutorService.submit方法。

<T> Future<T>submit(Callable<T> task)

Submits a value-returning task for execution and returns a Future representing the pending results of the task.
返回的是Future对象。

public interface Future<V>
Future represents the result of an asynchronous computation.
Future代表的是异步计算的结果。

然后我们用了ArrayList来存储Future,通过foreach语法遍历。

V get()      throws InterruptedException,             ExecutionException
Waits if necessary for the computation to complete, and then retrieves its result.
get方法会找回结果。


4)休眠

记得以前看过一个排序算法是用休眠写的,很有趣,至今还记得,就是让大的数睡久一点才输出。

Mythread的修改:

public void run() {    while(number-->0){        System.out.println(status());        try {            //Thread.sleep(100);            TimeUnit.MILLISECONDS.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

Thread.sleep是老版本的写法,TimeUnit.MILLISECONDS.sleep是SE5版本后的写法。其实后者也是用了Thread.sleep。

public void sleep(long timeout) throws InterruptedException {      if (timeout > 0) {         long ms = toMillis(timeout);         int ns = excessNanos(timeout, ms);         Thread.sleep(ms, ns);      }}

翻到火龙果的论坛帖子,其实后者和前者的区别就是可以变换单位,

TimeUnit.MICROSECONDS.sleep的话就是微秒,如果1000微秒的话那就是一毫秒,直接可以用TimeUnit.MILLISECONDS.sleep(1)。

这样就提供的更好的阅读性,不是有些题目会问你Thread.sleep方法里面的单位是多少吗?——毫秒。


5)优先级调度

public class TestPriority {    public static void main(String[] args) {        Thread t1 = new Thread(new MyThread());        Thread t2 = new Thread(new MyThread());        Thread t3 = new Thread(new MyThread());        t1.setPriority(Thread.MAX_PRIORITY);        t2.setPriority(Thread.MIN_PRIORITY);        t1.start();        t2.start();        t3.start();    }}

其实调度的映射做得不是很好,setPriority有10级优先级,1到10,数字越大优先级别越大。映射不好是指4不一定比5慢。


6)让步

让步的话就是前面写到的Thread.yield,给线程调度机制的信息就是我自己已经运行的差不多了,你们谁需要就谁去用吧。


这些是线程的基本概念,优先级,睡眠,之前没用过的就是Executor和TimeUnit.MILLISECONDS.sleep。

1 0