Java进阶-线程
进程和线程
这两个概念其实还是很好区分的,我们每一个应用程序其实就可以说是一个进程
,进程是由系统创建的;在每个进程之中又有多条线程,线程
是依赖进程而存在的。
java是无法直接调用系统程序的,只能调用C/C++
写好的程序去实现多线程。
多线程的实现
要实现多线程有两种方法,一个是继承Thread类,并重写run方法。还有一个是实现Runnable接口,该接口有效的避免了java单继承的局限性,适合多个相同的程序代码去处理同一个资源的情况。
我们来依次看一下两种实现方法:
Thread类
要实现多进程就要继承Thread类并且重写run方法,这里先看一个最简单的例子。
public class ThreadDemo extends Thread{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
ThreadDemo td2 = new ThreadDemo();
// td.setName("永琪");
// td2.setName("小燕子");
td.start();
td2.start();
}
public void run(){
System.out.println(getName());
}
}
/*
Thread-0
Thread-1
永琪
小燕子
当线程对象执行的时候就会调用run方法打印出当前线程对象的名字,默认就是Thread-x
。我们也可以设置线程名字。注释代码放开之后打印的就是永琪和小燕子。
-
getName():获取当前线程对象的名字。
-
setName():设置线程的名字。
-
start():开始执行线程
这里线程的执行顺序是随机的,并不是依次执行。多执行几次或者执行次数多一些会发现没有顺序规律。
线程休眠
写到这里不由得想到前几的天看到的那个段子
![](https://img.haomeiwen.com/i3473978/c5544bb9572c3467.jpg)
万万没想到竟然可以把休眠用的如此清新脱俗,“这么有想法的程序员怎么能开除?”。休眠就是让程序暂停执行直到指定的时间之后再开始。我们来修改一下上面的run方法:
public void run(){
int a = 0;
while (a < 10){
System.out.println(getName());
a++;
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
在run方法里加一个循环,让getName()执行十次,每次打印之后加一个休眠1秒的操作。这里休眠方法需要捕获异常。可以看到程序会在打印一次“小燕子”“永琪”(也可能是永琪、小燕子)之后暂停一秒,在继续打印,如此重复十次。
Runnable接口
同样实现Runnable接口的线程启动时也会先调用run方法。但是在Runnable接口中没有提供getName方法,我们需要通过Thread.currentThread().getName()
来获取进程名。
在创建进程时需要把Runnable的一个实例传递到Thread类的构造方法中,第二个参数接收一个进程名。
// 实现Runnable接口:
public class RunnableDemo implements Runnable {
public void run(){
for (int i = 0; i<10 ;i++){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
// 创建线程
Thread thread = new Thread(new RunnableDemo(), "实现接口的线程");
thread.start();
常用方法
1. 加入线程
如果我不想让这几个线程交叉执行,我想让“永琪”线程执行完再执行“小燕子”线程和接口线程。这里可以使用join()方法(需要捕获异常)。
td.start();
try {
td.join();
}catch (Exception e){
e.printStackTrace();
}
2. 停止线程
停止线程难道不是stop吗?还用说吗?是stop没错,不过让线程戛然而止的的效果不推荐使用。一般用不到此方法。
还有一个方法是interrupt()
,该方法终止线程状态,并抛出一个InterruptedException
异常。
这里我们可以声明一个volatile
关键字修饰的变量。被volatile
修饰的变量可以保证被每个线程都能读取到最新的值。我们就可以通过改变该变量的值来停止线程。再来修改一下run方法:
volatile boolean isStop = false;
public void run(){
int a = 0;
while (!isStop){
System.out.println(getName());
if(++a == 8){
isStop = true;
}
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
这样,当执行第8次的时候,isStop 被改为了true,所以run方法内的循环不在执行。停止了线程。
二者区别
Runnable可以避免单继承的限制,也可以实现多个线程共享资源。