进程和线程
进程:正在运行的程序实例,是资源分配的基本单位。例如我打开了英雄联盟 lol.exe,就启动了一个进程。
多进程:在我启动 lol.exe 之后,又打开了一个 dota.exe,即多个程序在同时运行。
线程:线程是进程的一部分,是调度和执行的基本单位,属于进程的一部分。例如在 lol.exe 这个进程中,盖伦攻击亚索就是一个线程实现的。
多线程:在同一时刻,盖伦攻击亚索,同时辛德拉在攻击盲僧,那么这就属于多线程。即一个进程中,多个相互独立或相互有协作关系的运行单元就是多线程。
三种线程创建方法
1. 继承Thread类
设计一个类,继承线程父类Thread,同时在这个类的内部重写run方法。看下面一段代码:
public static class MyThread extends Thread{
int num;
int id;
public MyThread(int num, int id) {
this.num = num;
this.id = id;
}
public void run() {
while(num > 0) {
System.out.println(this.id + " : " + this.num--);
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread thread1 = new MyThread(100, 1);
thread1.start();
MyThread thread2 = new MyThread(100, 2);
thread2.start();
}
在上面的代码中,我们创建了MyThread类,继承Thread类并重写了run方法。在main函数中,实例化了两个MyThread的对象,调用多个start方法即可启动多个线程。
2. 实现Runnable接口
设计一个类MyThread,让它实现Runnable接口,重写里面的run方法。在调用的时候,先创建一个类MyThread的对象,再创建一个Thread对象,并把MyThread的对象作为构造方法的参数传给Thread。看一段代码:
public static class MyThread implements Runnable{
int num;
int id;
public MyThread(int num, int id) {
this.num = num;
this.id = id;
}
public void run() {
while(num > 0) {
System.out.println(id + " : " + num--);
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread thread1 = new MyThread(100, 1);
Thread t1 = new Thread(thread1);
MyThread thread2 = new MyThread(100, 2);
Thread t2 = new Thread(thread2);
t1.start();
t2.start();
}
上面的代码比较简单,值得注意的是由于MyThread类实现了Runnable接口,因此他有run方法,并且需要重写。实例化MyThread得到了对象thread1,它要作为构造方法的参数传递到new的Thread中,最后调用start即可启动线程。
3. 匿名内部类
Thread t1 = new Thread() {
int id = 1;
int num = 100;
public void run() {
while(num > 0) {
System.out.println(id + " : " + num);
num--;
}
}
};
在创建一个Thread对象的后面跟上大括号,大括号中重写run方法,比较容易理解。
常见线程方法
1. sleep
sleep方法是让当前的线程暂停,sleep(1000)代表暂停1000毫秒。下面看一段代码:
Thread t1 = new Thread() {
int id = 1;
int num = 100;
public void run() {
while(num > 0) {
System.out.println(id + " : " + num);
if(num == 70) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
num--;
}
};
这段代码表示创建一个线程t1,在成员num为70的时候,该线程休眠1秒。注意,sleep可能会抛出InterruptedException 中断异常,因此需要在try-catch块中运行。
2. join
join是将当前线程添加到主线程中。那么什么是主线程呢?
对于所有的进程,至少有一个线程为主线程,那么在main函数开始执行的时候,就有一个看不见摸不着的主线程存在了。
那么通过join方法,就可以将当前线程加入到主线程,即主线程需要等待该线程运行结束,才能往下运行。
看一下下面的代码:
Thread t1 = new Thread() {
int id = 1;
int num = 100;
public void run() {
while(num > 0) {
System.out.println(id + " : " + num);
num--;
}
}
};
t1.start();
//将t1加入主线程
try {
t1.join();
}catch (Exception e) {
e.printStackTrace();
}
Thread t2 = new Thread() {
int id = 2;
int num = 100;
public void run() {
while(num > 0) {
System.out.println(id + " : " + num);
if(num == 70) {
//yield表示暂停线程,让其他线程获得更多CPU资源
Thread.yield();
}
num--;
}
}
};
t2.start();
在代码的中间,我们通过调用join方法,将线程t1加入了主线程。因此需要将t1线程运行完才能接着去运行下面的t2线程。
3. setPriority
给线程设置优先级,优先级越高越容易得到CPU资源,其中最高的优先级为MAX_PRIORITY,最低的为MIN_PRIORITY。
4. setDaemon
将线程设置为守护线程。
Java中存在两种线程,即守护线程和用户线程(非守护线程),之前所说的都是用户线程。当进程中所有的用户线程都消失的时候,守护线程也跟着销毁,当前进程结束。