一、线程和进程
1. 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。
2. 线程:一条线程指的是进程中一个单一顺序的控制流。
一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
二、创建线程方式
1. 设计一个类MyThread 继承Thread类,并且重写run方法。 实例化一个MyThread对象,并且调用其start方法,来启动线程。
public class MyThread01 extends Thread{
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
public static void main(String[] args) {
MyThread01 t1 = new MyThread01();
MyThread01 t2 = new MyThread01();
t1.start();
t2.start();
}
}
2. 创建一个类MyRunnable,实现Runnable接口,并且重新run方法。实例化一个MyRunnable对象,然后根据该对象创建一个Thread对象,调用其start方法,来启动线程。(推荐)
public class MyThread02 implements Runnable{
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
public static void main(String[] args) {
MyThread02 mt1 = new MyThread02();
MyThread02 mt2 = new MyThread02();
Thread t1 = new Thread(mt1);
Thread t2 = new Thread(mt2);
t1.start();
t2.start();
}
}
以上两种方式也可以通过匿名类的方式来实现。匿名类的好处是可以很方便的访问外部的局部变量(JDK7以前需要将外部的局部变量声明为final)。
public class MyThread{
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
};
t1.start();
Thread t2 = new Thread() {
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
};
t2.start();
}
}
public class MyThread{
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
public void run() {
for(int i=0; i<50; i++) {
System.out.println("i = " +i+ " 子线程ID:"+Thread.currentThread().getId());
}
}
});
t2.start();
}
}
二、常用方法
1. 当前线程暂停
Thread.sleep(1000); 表示当前线程暂停1000毫秒 ,其他线程不受影响。
Thread.sleep(1000); 会抛出InterruptedException 中断异常,因为当前线程sleep的时候,有可能被停止,这时就会抛出 InterruptedException。
2. 加入到当前线程中
t1.join(); t1线程加入到main线程中来,只有t1线程运行结束,才会继续往下走。
3. 线程优先级
一定要在线程启动前设置线程优先级。线程优先级越高,理论上获取CPU资源越多,但不能100%保证,因为线程调度是不能控制的。
t1.setPriority(Thread.MAX_PRIORITY); t1线程设置为最高优先级
t2.setPriority(Thread.MIN_PRIORITY); t2线程设置为最低优先级
4. 临时暂停
Thread.yield(); 临时暂停,使得其它线程可以占用CPU资源。
5. 守护线程
守护线程的概念是: 当一个进程里,所有的线程都是守护线程的时候,结束当前进程。守护线程就相当于公司的支持部门,如果一个进程只剩下守护线程,那么进程就会自动结束。
守护线程设置必须在线程开始前调用,如果线程开始后再设置为守护线程,则没有效果。
守护线程通常会被用来做日志,性能统计等工作。
t1.setDaemon(true);
6. 前台线程和后台线程
前台线程、后台线程是一种相对的概念。新创建的线程,默认都是前台线程。如果,某个线程对象在启动之前,调用了setDaemon(true)语句,这个线程就变成了一个后台线程。
前台线程和后台线程的区别和联系:
1、后台线程不会阻止进程的终止。属于某个进程的所有前台线程都终止后,该进程就会被终止。所有剩余的后台线程都会停止且不会完成。
2、可以在任何时候将前台线程修改为后台线程,方式是设置Thread.IsBackground 属性。
3、不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。
4、托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程。
前台线程和后台线程唯一区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。