线程基础知识系列(一)线程的建立和启动

并发编程式JAVA的一个相对较复杂的模块。纵观各招聘网站的要求,纷纷要求要具有高并发,高性能开发经验。这一块的知识,不得不学啊。学习并发有断断续续,有很长一段时间了,相关并发书籍也看了很多。感受有些收获,特开系列文章,分享下本身的学习收获。html

线程的内容涉及的内容特别多,仅几篇文章不可能透彻阐明,我的又不喜欢弄个大块头文章,因此仍是作个系列文章吧。做为系列的第一篇,本章主要内容:
java

  1. 线程VS进程web

  2. 并行VS并发数据库

  3. 线程的建立和启动
    编程

  4. user thread VS daemon thread
    tomcat

  5. 返回结果类型的线程安全

  6. join操做多线程

1.进程 &线程

process.jpg

进程

进程有独立的地址空间,对应于系统的程序和软件。好比QQ,word java tomcat web应用,一个进程崩溃,不会影响其余进程。属于重量级的资源管理方式。进程切换时,耗费资源较大,效率要差一些。并发

线程ide

而线程只是一个进程中的不一样执行路径。一个进程,能够包含多个线程。线程有本身的堆栈和局部变量,而多个线程共享内存。线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。通常状况下main()做为应用程序的入口。属于轻量级别的资源管理方式。

2.并行VS并发

 并发:一个处理器同时处理多个任务。经过CPU分片执行任务,属于逻辑上的同时发生。好比:一我的同时吃三个馒头。

 并行:多个处理器或者是多核的处理器同时处理多个不一样的任务。物理上的同时发生。好比:三我的同时吃三个馒头。

并行的前提:具有多个处理器和多核的处理资源。

并发的前提:在资源CPU能力必定的基础上,区分I/O密集型任务,CPU密集型任务,尽可能减小CPU等待时间,压榨CPU的处理极限。

JAVA领域,涉及的都是并发。

3.线程的建立和启动

JAVA的线程有2中方式

1.实现 java.lang.Runnable接口
2.继承java.lang.Thread类

HeavyWorkRunnable.java,实现 java.lang.Runnable接口

package com.threadexample.base;

public class HeavyWorkRunnable implements Runnable {
 
    @Override
    public void run() {
        System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
    }
 
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
 
}

MyThread.java,继承java.lang.Thread类

package com.threadexample.base;

public class MyThread extends Thread {
 
    public MyThread(String name) {
        super(name);
    }
 
    @Override
    public void run() {
        System.out.println("MyThread - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("MyThread - END "+Thread.currentThread().getName());
    }
 
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
     
}

测试代码ThreadRunExample

package com.threadexample.base;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadRunExample {
 
    public static void main(String[] args){
        Thread t1 = new Thread(new HeavyWorkRunnable(), "t1");
        Thread t2 = new Thread(new HeavyWorkRunnable(), "t2");
        System.out.println("Starting Runnable threads");
        t1.start();
        t2.start();
        System.out.println("Runnable Threads has been started");
        Thread t3 = new MyThread("t3");
        Thread t4 = new MyThread("t4");
        System.out.println("Starting MyThreads");
        t3.start();
        t4.start();
        System.out.println("MyThreads has been started");

        ExecutorService executorService = Executors.newCachedThreadPool();
        Thread t5 = new Thread(new HeavyWorkRunnable(), "t5");
        HeavyWorkRunnable t6 = new HeavyWorkRunnable();
        executorService.submit(t5);//启动
        executorService.execute(t6);//启动
        executorService.shutdown();
    }
}

代码展现了2种建立线程的方式,以及多种启动方式。须要注意的是ExecutorService API,ExecutorService 的设计是为了尽量实现线程的复用,由于线程也会占用资源,线程复用能够下降资源占用,提高性能,是并发优化的一个途径,固然使用ThreadFactory的工厂,为线程管理提供了便利。

Runnable vs Thread
1.不只仅提供执行任务,还提供额外功能,实现 java.lang.Runnable接口
2.若是类的惟一功能是做为线程执行,能够继承java.lang.Thread
须要注意,thread 提供的方法没有返回值。

------------------------------

执行结果

Starting Runnable threads
Runnable Threads has been started
Starting MyThreads
MyThreads has been started
MyThread - START t3
Doing heavy processing - START t2
MyThread - START t4
Doing heavy processing - START t1
Doing heavy processing - START pool-1-thread-1
Doing heavy processing - START pool-1-thread-2
MyThread - END t3
Doing heavy processing - END t1
MyThread - END t4
Doing heavy processing - END t2
Doing heavy processing - END pool-1-thread-1
Doing heavy processing - END pool-1-thread-2

经过结果分析:线程实际执行结果,和程序编写顺序不一致。并且屡次运行执行的结果也不一样。这就是多线程,执行结果的不肯定性。

4.user thread VS daemon thread

对上面的ThreadRunExample ,进行DEBUG跟踪。

wKiom1dvlgej-_eRAABM-qR14cc719.png

threads有些是本身建立的,有些不是本身建立的,如Finalizer线程,负责垃圾收集线程,是虚拟机自动建立的线程。

导出thread信息,部分信息截图

wKiom1dvl0LSY1GuAAAqTUsTeTU303.png

经过两个线程对比,Finalizer是daemon线程,优先级是8. 属于 “user thread"

咱们建立的线程不是daemon线程,优先级是5.属于"daemon thread".

所谓守护线程(daemon thread),是指在程序运行的时候在后台提供一种通用服务的线程,好比垃圾回收线程就是一个很称职的守护者,而且这种线程并不属于程序中不可或缺的部分。因 此,当全部的非守护线程结束时,程序也就终止了,同时会杀死进程中的全部守护线程。反过来讲,只要任何非守护线程还在运行,程序就不会终止。

用户线程和守护线程二者几乎没有区别,惟一的不一样之处就在于退出时机的差异:若是用户线程已经所有退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 由于没有了被守护者,守护线程也就没有工做可作了,也就没有继续运行程序的必要了。
将线程转换为守护线程能够经过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时须要注意一下几点:
(1) thread.setDaemon(true)必须在thread.start()以前设置,不然会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,由于它会在任什么时候候甚至在一个操做的中间发生中断。

JavaDaemonThread.java代码

package com.threadexample.base;

public class JavaDaemonThread {
 
    public static void main(String[] args) throws InterruptedException {
        Thread dt = new Thread(new DaemonThread(), "dt");
        dt.setDaemon(true);
        dt.start();
        //continue program
        Thread.sleep(30000);
        System.out.println("Finishing program");
    }
 
}
 
class DaemonThread implements Runnable{
 //此处是个死循环(若是不是daemon线程,他会一直执行下去,永不退出;
 //对于daemon线程,它会随user thread一并退出
    @Override
    public void run() {
        while(true){
            processSomething();
        }
    }
 
    private void processSomething() {
        try {
            System.out.println("Processing daemon thread");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
     
}

执行结果

Processing daemon thread
Processing daemon thread
Processing daemon thread
Processing daemon thread
Processing daemon thread
Processing daemon thread
Finishing program

5.返回结果类型的线程(Callable Future)

下面是Runnable 接口.执行方法没有返回值。

public interface Runnable {
    public abstract void run();
}

若是在面对执行计算任务是,须要有返回值怎么办?

也是能够的,之前面的例子稍加改造。

HeavyWorkRunnableWithResult.java

package com.threadexample.base;

public class HeavyWorkRunnableWithResult implements Runnable {
    private String result;
    public String getResult(){
        return result;
    }
    @Override
    public void run() {
        System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
            this.result="执行结果="+Thread.currentThread().getName();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
    }
 
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
 
}

测试代码ThreadRunExampleWithResult.java

package com.threadexample.base;
public class ThreadRunExampleWithResult {
    public static void main(String[] args){
        System.out.println("Runnable Threads has been started");
        HeavyWorkRunnableWithResult runnableWithResult = new HeavyWorkRunnableWithResult();
        Thread t1 = new Thread(runnableWithResult,"t1");
        t1.start();
        System.out.println("输出结果:"+runnableWithResult.getResult());
    }
}

打印结果

------------------------

Runnable Threads has been started
输出结果:null
Doing heavy processing - START t1
Doing heavy processing - END t1

--------------------------

经测试结果不是咱们想要的结果,返回值为空。这是由于在打印输出结果时,线程尚未执行完。

怎么修复这个问题呢。


升级后的HeavyWorkRunnableWithResult.java,添加了个完成标记字段。

package com.threadexample.base;

public class HeavyWorkRunnableWithResult implements Runnable {
    private String result;
    private volatile boolean done=false;
    public String getResult(){
        return result;
    }
    public boolean isDone(){
        return  this.done;
    }
    @Override
    public void run() {
        System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
            this.result="执行结果="+Thread.currentThread().getName();
            this.done=true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
    }
 
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
 
}

注意必定要将done 设置为volatile 类型,不然main线程永远不会读取不到更新的值。关于volatile
,又涉及到底层的JMM模型了,不在此涉及了。

改造后的ThreadRunExampleWithResult.java,加个循环,保证有结果以后打打印。

package com.threadexample.base;
public class ThreadRunExampleWithResult {
    public static void main(String[] args){
        System.out.println("Runnable Threads has been started");
        HeavyWorkRunnableWithResult runnableWithResult = new HeavyWorkRunnableWithResult();
        Thread t1 = new Thread(runnableWithResult,"t1");
        t1.start();
        while(!runnableWithResult.isDone()){
        //空循环
        }
        System.out.println("输出结果:"+runnableWithResult.getResult());
    }
}

执行结果

-----------------------------

Runnable Threads has been started
Doing heavy processing - START t1
Doing heavy processing - END t1
输出结果:执行结果=t1

-----------------------------

仔细想一想,咱们作了好多无用的事,好比完成判断,循环等待。JAVA专为解决此类问题,提供了Callable接口。

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

HeavyWorkCallable.java 做为HeavyWorkRunnableWithResult 的Callable版本

package com.threadexample.base;

import java.util.concurrent.Callable;

public class HeavyWorkCallable implements Callable<String> {
    @Override
    public String call() {
        System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
            return "执行结果="+Thread.currentThread().getName();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
 
}


ThreadRunExampleCallable.java 测试类

package com.threadexample.base;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class ThreadRunExampleCallable {
    public static void main(String[] args){
        System.out.println("Runnable Threads has been started");

        //Get ExecutorService from Executors utility class, thread pool size is 10
        ExecutorService executor = Executors.newFixedThreadPool(5);
        //create a list to hold the Future object associated with Callable
        List<Future<String>> list = new ArrayList<Future<String>>();
        //Create MyCallable instance
        Callable callable = new HeavyWorkCallable();
        for(int i=0; i< 10; i++){
            //submit Callable tasks to be executed by thread pool
            Future<String> future = executor.submit(callable);
            //add Future to the list, we can get return value using Future
            list.add(future);
        }
        for(Future<String> fut : list){
            try {
                //print the return value of Future, notice the output delay in console
                // because Future.get() waits for task to get completed
                System.out.println(new Date()+ "::"+fut.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        //shut down the executor service now
        executor.shutdown();
//        System.out.println("输出结果:"+runnableWithResult.getResult());
    }
}

执行结果以下

-----------------------
Runnable Threads has been started
Doing heavy processing - START pool-1-thread-1
Doing heavy processing - START pool-1-thread-2
Doing heavy processing - START pool-1-thread-3
Doing heavy processing - START pool-1-thread-5
Doing heavy processing - START pool-1-thread-4
Doing heavy processing - START pool-1-thread-3
Sun Jun 26 17:41:51 CST 2016::执行结果=pool-1-thread-1
Doing heavy processing - START pool-1-thread-5
Sun Jun 26 17:41:57 CST 2016::执行结果=pool-1-thread-2
Doing heavy processing - START pool-1-thread-1
Sun Jun 26 17:41:57 CST 2016::执行结果=pool-1-thread-3
Doing heavy processing - START pool-1-thread-4
Doing heavy processing - START pool-1-thread-2
Sun Jun 26 17:41:57 CST 2016::执行结果=pool-1-thread-4
Sun Jun 26 17:41:57 CST 2016::执行结果=pool-1-thread-5
Sun Jun 26 17:41:57 CST 2016::执行结果=pool-1-thread-3
Sun Jun 26 17:42:03 CST 2016::执行结果=pool-1-thread-1
Sun Jun 26 17:42:03 CST 2016::执行结果=pool-1-thread-5
Sun Jun 26 17:42:03 CST 2016::执行结果=pool-1-thread-2
Sun Jun 26 17:42:03 CST 2016::执行结果=pool-1-thread-4

Process finished with exit code 0

-----------------------

6.join操做

Java Thread join方法能够用于直到指定线程死亡时中止当前线程。有三个重载方法

public final void join()

public final synchronized void join(long millis): 中止当前线程直到线程死时或者超过millis时间。

public final synchronized void join(long millis, int nanos)

咱们能够对5节中的ThreadRunExampleWithResult,进行改造下,达到想要的效果

package com.threadexample.base;
public class ThreadRunExampleJoin {
    public static void main(String[] args){
        System.out.println("Runnable Threads has been started");
        HeavyWorkRunnableWithResult runnableWithResult = new HeavyWorkRunnableWithResult();
        Thread t1 = new Thread(runnableWithResult,"t1");
        t1.start();
//        while(!runnableWithResult.isDone()){
//        //空循环
//        }
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("输出结果:"+runnableWithResult.getResult());
    }
}

join方法通常使用在“直到....才”的场景。和自带的CountDownLatch工具类有重合的部分。

输出结果

----------

Runnable Threads has been started
Doing heavy processing - START t1
Doing heavy processing - END t1
输出结果:执行结果=t1

---------


引用资源

http://www.journaldev.com/1024/java-thread-join-example

http://www.cnblogs.com/luochengor/archive/2011/08/11/2134818.html

http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html

http://www.journaldev.com/1016/java-thread-example-extending-thread-class-and-implementing-runnable-interface


 最后

本文主要讲了线程的几点用法,比较简单。

Runnable ->Thread->Daemon ->Callable-->Join。捎带着提了下violatile关键字。

因为线程间没有共享变量,因此不涉及到一丁点线程安全的东西。后面专门抽出几篇讲解下。

附件为代码

wKiom1dvqODB350YAAAgH5tjGVI443.png

相关文章
相关标签/搜索