Java之线程基础
简介
本文主要介绍Java编程中线程的基础知识,包括线程介绍、线程的五大状态、线程的三种创建方式、线程的同步,最后根据线程的经典问题——生产者消费者模型,给出了实现的源代码。
通过本文的学习,可以掌握基本的Java多线程编程。
一、线程介绍
若将操作系统中的任务看成进程,那么任务中的多个执行流可以看做是多个线程。
进程是系统进行资源分配和调度的一个独立单元。
线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。
二、线程的五大状态
每个线程都有自己的生命周期,通常将生命周期分为5个状态(阶段),即新建(New)、就绪(Ready)、运行(Running)、阻塞(Blocked)、死亡(Dead)。
1、新建状态
当使用new关键字新创建一个Thread对象时,该线程就进入了新建状态。JVM会为新建的线程分配内存、并初始化线程对象的成员变量,线程对象的执行体还未启动。
2、就绪状态
当一个线程对象执行了start()成员方法时,该线程便进入了就绪状态。JVM开始为线程对象创建方法调用栈和程序计数器。此时线程对象同样没有运行,只代表线程对象可以运行,运行时机取决于JVM的线程调度器。
3、运行状态
当JVM调度器调度就绪状态的线程对象时,即执行线程对象的run()方法,那么该线程对象进入了运行状态。
对於单个处理器中存在多个线程时,处理器采用时间片轮换的方式来执行各个线程。
4、阻塞状态
为了使多个线程都可到执行,那么处于运行状态的线程对象不可能一直处于运行状态。当被中断时,线程对象从运行状态变为了阻塞状态。
线程进入阻塞状态的情况:
- 遇到 sleep();
- 遇到 join();
- 遇到 wait();
- 遇到 read()、write();
5、死亡状态
当线程生命周期结束时,线程对象便进入了死亡状态,进入死亡状态的线程不能重进入其它任何状态,只能静静等着JVM垃圾回收机制来回收自己。
三、线程的三种创建方式
JDK中提供了三种创建线程对象的方式,如继承Thread、实现Runnable、实现Callable。
1、继承Thread
线程对象类直接继承Thread类,并重写Thread类的run()方法。如下所示:
public class ThreadTest extends Thread{
@Override
public void run() {
// 执行体代码
}
}
该线程对象的运行,如下所示:
public static void main(String[] args) {
Thread thread01 = new ThreadTest();
thread01.start();
}
2、实现Runnable
Runnable是一个接口,线程对象类可以实现该接口,并重写Runnable接口的run()方法。如下所示:
public class ThreadTest implements Runnable{
@Override
public void run() {
// 执行体代码
}
}
该线程对象的运行,如下所示:
public static void main(String[] args) {
ThreadTest thread01 = new ThreadTest();
new Thread(thread01, "Thread-name").start();
}
3、实现Callable
实现Callable接口的线程,创建过程如下:
- 1.创建一个线程,创建Callable的实现类Race,并且重写call方法;
- 2.得到Future对象;
- 3.获取返回值;
- 4.停止服务;
//返回Interger类型
public class ThreadTest implements Callable<Integer>{
@Override
public Integer call() {
//执行体代码
return Integer;
}
}
该线程对象的运行,如下所示:
public static void main(String[] args) {
ExecutorService ser = Executors.newFixedThreadPool(2);
ThreadTest threadTest = new ThreadTest();
//获取值
Future<Integer> result = ser.submit(threadTest);
try {
int numR = result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//停止服务
ser.shutdown();
}
四、线程的同步
在多线程的情况下,会存在对公共资源进行访问的行为,也就是资源的竞争关系,其中同步就是多线程编程中处理竞争关系的一种方法,它使多线程访问公共资源时是安全有效的。同步使用synchronized关键字,其中又分为synchronized方法和synchronized块。
1、synchronized方法
用synchronized关键字修饰类的一个方法,如下所示:
public synchronized void seats() {
//需要同步的对象属性操作代码
}
用synchronized关键字修饰的一个作用块,如下所示:
synchronized(ThreadTest.class) {
//需要同步的对象属性操作代码
}
五、生产者消费者实例
多线程编程中,最经典的问题就是生产者消费者问题:
生产者负责生产产品,消费者购买产品,当库存为空的时候,生产者继续生产,消费者购买数量要小于等于库存上限,不能出现,生产一次,消费两次,或者生产了没有消费的情况。
我的代码实现如下:
import java.util.ArrayList;
import java.util.List;
public class Cooperation {
public static void main(String[] args) {
// TODO Auto-generated method stub
PipeHandle pipe = new PipeHandle();
Productor productor = new Productor(pipe);
Customer customer = new Customer(pipe);
new Thread(productor,"Productor").start();
new Thread(customer,"Customer").start();
}
}
//生产者
class Productor implements Runnable{
PipeHandle pipe;
public Productor(PipeHandle pipe) {
super();
this.pipe = pipe;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i < 100; i++) {
Food food = new Food();
pipe.push(food);
System.out.println("Productor is making " + i +" food");
}
}
}
//消费者
class Customer implements Runnable{
PipeHandle pipe;
public Customer(PipeHandle pipe) {
super();
this.pipe = pipe;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i < 100; i++) {
Food food = pipe.pop();
System.out.println("Coustomer is get "+i+" food");
}
}
}
//商品缓存区管道
class PipeHandle{
Food[] pipe = new Food[10];
int count;
public PipeHandle( ) {
}
public synchronized void push(Food food) {
if(count >= 10) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
pipe[count++] = food;
this.notifyAll();
}
public synchronized Food pop() {
Food food = null;
if(count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
food = pipe[--count];
this.notifyAll();
return food;
}
}
//商品
class Food{
}