Java中给多线程编程提供了内置的支持,多线程是多任务的一种特别形式,它使用了更小的资源开销。这里须要知道两个术语及其关系:进程和线程。html
进程:进程是系统进行资源分配和调度的一个独立单位。一个进程包括由操做系统分配的内存空间,包含一个或多个线程。java
线程:线程是进程的一个实体,是CPU调度和分派的基本单位。它可与同属一个进程的其余的线程共享进程所拥有的所有资源。数据库
(一)线程的生命周期编程
线程是一个动态执行的过程,从产生到死亡,这个过程称为线程的生命周期。线程的状态有:新建状态、就绪状态、运行状态、阻塞状态、死亡状态,以下图所示,注意整个执行过程的实现:安全
(二):线程的建立多线程
Java提供了三种建立线程的方法:并发
在开发中,前两种是经常使用的线程建立方式,下面来简单说下:ide
(1)经过实现Runnable接口建立线程函数
public class RunnableDemo implements Runnable{ private Thread t; private String threadName; //构造方法 public RunnableDemo(String name) { this.threadName = name; System.out.println("建立线程:"+threadName); } //重写run()方法 @Override public void run() { System.out.println("运行线程:"+threadName); try { for(int i=4;i>0;i--){ System.out.println("Thread: "+threadName+","+i); Thread.sleep(50); } }catch (Exception e) { System.out.println("Thread "+threadName+" 阻塞"); } System.out.println("Thread "+threadName+" 终止"); } //调用方法(为了输出信息,能够忽略) public void start(){ System.out.println("启动线程:"+threadName); if(t == null){ t = new Thread(this,threadName); t.start(); } } }
测试类:工具
public class RunnableTest { public static void main(String[] args) { RunnableDemo r1 = new RunnableDemo("T1"); r1.start(); RunnableDemo r2 = new RunnableDemo("T2"); r2.start(); } }
输出为:
建立线程:T1 启动线程:T1 建立线程:T2 启动线程:T2 运行线程:T1 Thread: T1 运行线程:T2 Thread: T2,4 Thread: T2,3 Thread: T1,3 Thread: T2,2 Thread: T1,2 Thread: T2,1 Thread: T1,1 Thread T2 终止 Thread T1 终止
从上面的实例能够看出,经过实现Runnable接口建立线程的几个要点:
(2)经过继承Thread类建立线程
它本质上也是实现了 Runnable 接口的一个实例,因此这里就不贴出代码了,能够按照上面的实例,更改class为继承便可,以下:
public class ThreadDemo extends Thread{}
Thread类的经常使用且重要的方法有:
注意:Java虚拟机容许应用程序并发的运行多个执行线程,利用多线程编程能够编写高效的程序,但线程太多,CPU 花费在上下文的切换的时间将多于执行程序的时间,执行效率反而下降,因此,线程并非建立的越多越好好,通常来讲小到1个,大到10左右基本就够用了。
固然,关于线程的其余知识,如优先级、休眠、终止等,这里就不作介绍了。
(三)synchronized关键字
Java提供了不少方式和工具来帮助简化多线程的开发,如同步方法,即有synchronized关键字修饰的方法,这和Java的内置锁有关。每一个Java对象都有一个内置锁,若方法用synchronized关键字声明,则内置锁会保护整个方法,即在调用该方法前,须要得到内置锁,不然就处于阻塞状态。一个简单的同步方法声明以下:
public synchronized void save(){}
synchronized关键字也能够修饰静态方法,此时若调用该静态方法,则会锁住整个类。下面经过实例来讲明下具体的使用:
同步线程类:
public class SyncThread implements Runnable { //定义计数变量并在构造函数中初始化 private static int count; public SyncThread(){ count = 0; } @Override public synchronized void run() { for(int i=0;i<5;i++){ //打印当前count值并进行累加操做,可分开写 System.out.println(Thread.currentThread().getName() +":"+ (count++)); try { Thread.sleep(100); }catch (InterruptedException e) { e.printStackTrace(); } } } public int getCount(){ return count; } }
测试类:
public class SyncTest { public static void main(String[] args) { SyncThread sThread = new SyncThread(); //建立线程对象的同时初始化该线程的名称 Thread t1 = new Thread(sThread,"SThread1"); Thread t2 = new Thread(sThread,"SThread2"); t1.start(); t2.start(); } }
输出为:
SThread1:0 SThread1:1 SThread1:2 SThread1:3 SThread1:4 SThread2:5 SThread2:6 SThread2:7 SThread2:8 SThread2:9
从上面能够看出:一个线程访问一个对象中的synchronized同步方法时,其余试图访问该对象的线程将被阻塞。固然,你们能够去掉synchronized关键字,看看会有什么不一样。这里必需要注意:是访问同一个对象的不一样方法,如上面的对象sThread,如果不一样的对象,则不受阻塞。这里不作介绍了,你们能够参考:Java中synchronized的用法,好好理解下。
(四)volatile关键字
相比较synchronized而言,volatile关键字是Java提供的一种轻量级的同步机制,为域变量的访问提供了一种免锁机制,使用volatile修饰域至关于告诉虚拟机该域可能会被其余线程更新,所以每次使用该域就要从新计算,而不是使用寄存器中的值。
若是读操做的次数要远远超过写操做,与锁相比,volatile 变量一般可以减小同步的性能开销。简单的定义以下:
private volatile int count = 0;
volatile不具有原子特性,也不能用来修饰final类型的变量。要使volatile修饰的变量提供理想的线程安全,必须知足两个条件:
这里不作详述了,但须要注意一点:避免volatile修饰的变量用于复合操做,如 num++,这个复合操做包括三步(读取->加1->赋值),因此,在多线程的环境下,有可能线程会对过时的num进行++操做,从新写入到主存中,而致使出现num的结果不合预期的状况。
线程间还能够实现通讯,这里不作介绍。
Java中的对象使用new操做符建立,若建立大量短生命周期的对象,则性能低下。因此才有了池的技术,如数据库链接有链接池,线程则有线程池。
使用线程池建立对象的时间是0毫秒,说明其高效性。你们感兴趣的可自行查看、了解该块的知识点。
好了,以上归纳的就是多线程的基本知识点了,但愿帮到你们。