进程和线程
返回目录
在学习多线程之前要理解两个概念:进程和线程.
进程
由操作系统管理的最基本的运行单元,每个进程都有独立的内存空间.比如浏览器,编辑器等都是一个进程.
线程
进程里独立运行的子任务.比如浏览器要处理页面显示,数据请求,文件下载等任务,如果没有给每个任务分配一个线程,那么这些任务只能轮循执行,不能同步执行,就会给用户造成延迟的感觉.给每个任务单独分配线程后,所有的任务都能在系统管理下,会给每个任务分配一定的执行时间,给用户的感觉就是所有的任务是同步执行的.当然这种分配每个操作系统所使用的方式是不一样的,有的是每个任务分配的时间一致,有的是优先级越高,获取的运行时间越多.
线程安全
返回目录
在线程安全的定义中,最核心的概念就是正确性和一致性,当多线程访问某个类时,都能表现出正确的行为,并且与单线程访问时是一致的,那么这便可以称为这个类是线程安全的.
无状态的对象一定是线程安全的.如果类方法中存在变量,由于类方法中的变量是存放在栈中的,不同的线程不会共享类方法中的变量.也就不会造成线程安全问题.
原子性
返回目录
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。将整个操作视作一个整体是原子性的核心特征.
比如以下例子.并不是一个原子操作.
包括了三个步骤:
1.从内存中读取count值;
2.将count值加1;
3.将修改后的count值写入内存中;
以上的三个步骤在多线程运行环境下,都有可能被打断,导致出现数据出错.
count++;
多线程实现
返回目录
创建线程有两种方式,一种是继承Thread.另一种是实现Runable接口.
从源码中可以看出.Thread实现了Runnable.Thread可以通过Runnable初始化一个线程.
不管用何种方式创建线程类,必须覆盖run()方法,run()方法就是线程运行时所执行的指令.
//Runnable接口定义,只有一个run()方法
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
//注册一个线程
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
//通过Runnable注册一个线程
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
//等其他变量和方法
}
继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("继承Thread实现多线程");
System.out.println("线程:" + Thread.currentThread().getName() + "运行." );
}
}
实现Runable接口
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable接口实现多线程");
System.out.println("线程:" + Thread.currentThread().getName() + "运行." );
}
}
测试
public static void main(String[] args) {
System.out.println("主线程:" + Thread.currentThread().getName() + "执行");
//继承Thread实现多线程
MyThread thread1 = new MyThread();
thread1.start();
//实现Runnable接口实现多线程
MyThread1 thread2 = new MyThread1();
Thread thread3 = new Thread(thread2);
thread3.start();
}
输出
主线程:main执行
继承Thread实现多线程
线程:Thread-0运行.
实现Runnable接口实现多线程
线程:Thread-1运行.
可以看到,系统给主线程分配的名字是main,其他的为Thread-x;
选用Thread还是Runnable实现
由于Java里面是单继承多接口,因此使用Runnable比较好.这样还可以继承其他类.
线程的运行
由以上可知,线程的运行使用start()方法而不是直接调用run()方法,前者由系统进行管理,分配运行时间,后者会直接运行.
线程的结束
1.当线程执行完run()方法后,该线程便结束.
2.当线程run()执行过程中,如果出现异常,线程也会结束.
线程的状态
返回目录
从上图可知线程有如下六种状态(该状态在java.lang.Thread中的内部枚举类state中进行定义):
- NEW-新创建
- RUNNABLE-可运行的状态
- BLOCKED-阻塞状态
- WAITING-等待状态
- TIMED_WAITING-带超时时间的等待状态
- TERMINATED-结束状态
NEW-新创建:
当new 一个thread后,并且没有调用thread.start();时所处的状态.
RUNNABLE-可运行的状态:
当执行thread.start();便进入可运行的状态,该状态包含两种状态准备和正在运行的状态,由于操作系统分配的时间片不固定,线程会在两个小状态之间来回切换.
BLOCKED-阻塞状态:
当该线程试图获取某个对象锁时,但是这个对象锁被其他线程所持有,那么该线程便会进入阻塞状态,直到获取到锁.
WAITING-等待状态 和 TIMED_WAITING-带超时时间的等待状态
线程进入等待状态.TIMED_WAITING是带了超时时间,如果超时时间到,就退出等待状态.
TERMINATED-结束状态:
1.当线程执行完run()方法后,该线程便结束.
2.当线程run()执行过程中,如果出现异常,线程也会结束.