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

先提前写并发这一块,前面的数组,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 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
2 9
2 8
2 7
2 6
2 5
2 4
2 3
2 2
2 1
2 zero
3 9
3 8
3 7
3 6
3 5
3 4
3 3
3 2
3 1
3 zero
4 9
4 8
4 7
4 6
4 5
4 4
4 3
4 2
4 1
4 zero

3)从任务中返回值

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

java.util.concurrent

Interface Callable<V>

Type Parameters:

V - the result type of method call

Modifier and Type Method and Description
V call()

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。

时间: 2024-09-30 07:22:03

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

《On Java 8》中文版,又名《Java 编程思想》中文第五版

来源:LingCoder/OnJava8 主译: LingCoder 参译: LortSir 校对:nickChenyx E-mail: [email protected] 本书原作者为 [美] Bruce Eckel,即(Thinking in Java 4th Edition,2006)的作者. 本书是事实上的 Thinking in Java 5th Edition(On Java 8,2017). Thinking in Java 4th Edition 基于 JAVA 5 版本:On

异常笔记--java编程思想

开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出异常.抛出异常后发生的几件事: 1.在堆上创建异常对象. 2.当前的执行路径中止                                          3. 当前环境抛出异常对象的引用.                                         4. 异常处理机制接

《Java编程思想(第4版)》pdf

下载地址:网盘下载 内容简介 编辑 本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形.从Java的基础语法到最高级特性(深入的面向对象概念.多线程.自动项目构建.单元测试和调试等),本书都能逐步指导你轻松掌握.[1] 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作.本书的作者拥有多年教学经验,对C.C++以及Java语言都有独到.深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

1.JAVA 编程思想——对象入门

对象入门 欢迎转载,转载请标明出处:    http://blog.csdn.net/notbaron/article/details/51040219 如果学JAVA,没有读透<JAVA 编程思想>这本书,实在不好意思和别人说自己学过JAVA.鉴于此,蛤蟆忙里偷闲,偷偷翻看这本传说中的牛书. 面向对象编程OOP具有多方面吸引力.实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.已于维护的设计方案. 这些描述看上去非常吸引人的,不过蛤蟆还是没啥印象(至少到

71.JAVA编程思想——JAVA与CGI

71.JAVA编程思想--JAVA与CGI Java 程序可向一个服务器发出一个CGI 请求,这与HTML 表单页没什么两样.而且和HTML 页一样,这个请求既可以设为GET(下载),亦可设为POST(上传).除此以外,Java 程序还可拦截CGI 程序的输出,所以不必依赖程序来格式化一个新页,也不必在出错的时候强迫用户从一个页回转到另一个页.事实上,程序的外观可以做得跟以前的版本别无二致. 代码也要简单一些,毕竟用CGI 也不是很难就能写出来(前提是真正地理解它).所以我们准备办个CGI 编程

Java编程思想(五) —— 多态(上)

上一章,Java编程思想(四) -- 复用类里面讲到了向上转型,感觉和多态放在一起写更好. 多态,polymorphism.一个重要的特性,篇幅太长了,分上下两篇写. (1)向上转型 class TV{ public static void show(TV tv){ System.out.println("TV"); } } public class LeTV extends TV{ public static void main(String[] args) { LeTV letv

66.JAVA编程思想——网络编程

66.JAVA编程思想--网络编程 历史上的网络编程都倾向于困难.复杂,而且极易出错. 程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识.一般地,我们需要理解连网协议中不同的"层"(Layer).而且对于每个连网库,一般都包含了数量众多的函数,分别涉及信息块的连接.打包和拆包:这些块的来回运输:以及握手等等.这是一项令人痛苦的工作.但是,连网本身的概念并不是很难.我们想获得位于其他地方某台机器上的信息,并把它们移到这儿:或者相反.这与读写文件非常相似,只是文件存在于远程

64.JAVA编程思想——优先级

64.JAVA编程思想--优先级 线程的优先级(Priority)告诉调试程序该线程的重要程度有多大.如果有大量线程都被堵塞,都在等候运行,调试程序会首先运行具有最高优先级的那个线程.然而,这并不表示优先级较低的线程不会运行(换言之,不会因为存在优先级而导致死锁).若线程的优先级较低,只不过表示它被准许运行的机会小一些而已. 可用getPriority()方法读取一个线程的优先级,并用setPriority()改变它.在下面程序中,大家会发现计数器的计数速度慢了下来,因为它们关联的线程分配了较低