在java学习当中,最经常遇到的就是面对多个任务需要同时执行或者在一个较集中的时间内内进行,那么这个时候最好的方法不是使用多个程序进行运行,而是在在这个程序当中使用多线程处理,在日常中有一个词更容易被大家提及,就是进程,进程和线程在java中并不是同一个东西值得注意的是,在这里面一个进程可以拥有一个以上的线程,而线程是不可以同时作用在两个进程上,而在不同的系统当中,对线程的处理不一定都是一样的
比如说在windows和linux中使用的是抢占式多任务,自己是无法在程序运行之后知道,而iOS是特殊的伪多任务处理,安卓系统好像也是抢占式多任务处理,但具体并没有找到相关的资料,这里不做过多的阐述,那么回归到本质的问题,多线程有什么好处,或者说,多进程有什么好处,
多线程的好处主要体现在开销上,相比于进程,县城往往会消耗更少的资源,这个是很好理解的,毕竟一个进程里面可能包括了不止一个线程。同时线程可以做到更快的通信,在读写同一个变量的时候,线程会比进程快数倍(忘了在哪里看到的资料了,不一定准确)
但进程也存在自己的好处,进程会比线程更加的稳定,,在考虑到多线程的情况下,多线程一旦存在任何一个线程崩溃都会导致整个多线程程序崩溃
在这之外要注意的是,在使用到多线程处理的时候,一定要对线程中的数据进行同步,避免出现几乎同时得到一个数据,在这个数据进行处理本应该进行两次的情况下只处理掉了一次。
创建新的线程
在Java创建新的线程的时候,需要使用Thread进行一个创建,然后再调用stat()方法进行调用,使线程得以运行
举一个例子
public class Main {
public static void main(String[] args){
Thread t=new Thread();
t.start();
}
}
这样子就可以创建一个新的线程,同时做到对这个线程启动,只不过在这里对线程什么都没有做,很快将会退出程序,如果想要运行这个程序里面的线程,存在很多方法,但有些方法作用比较相似,使用起来也差不多,所以可以的话最好对自己的书写习惯进行固定,这样对代码阅读者更加的友好,(主要是在学习的时候如果遇到这样不同的写法可能会对其他人的学习存在一定的误解),那么我如果需要的不止这些,创建线程是为了帮助我实现更多的事情,也让我更方便的实现我代码中费时简单的工作,所以一般在创建线程之后,更多的是对其中分配任务,
值得注意的是,在创建完成打算启动的时候需要使用start()方法进行启动新的线程,但在使用线程的时候,方法名并不是start,而是需要使用run进行调用,同时这是系统给的一个接口,所以在写run之前需要先使用@Override确定需要改写的内容那么现在举一个例子
public class Main1 {
public static void main(String[] args){
Thread t =new Thread();
t.start();
}
class thread extends Thread{
@Override
public void run(){
System.out.println("hello word");
}
}
}
还有其他两种办法,我就不进行一一阐述了,这里直接将两种方法的例子放在这里
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});
t.start(); // 启动新线程
}
}
这里借用了一下 廖雪峰老师的代码,可以看出来其他两种方法并不一定会比第一种方法有多少优势,可能和c里面的for和while相似,主要是在语法上存在一定的区别
那么在正常的单一线程中调用和在多线程中调用是否存在区别,这个办法很好解决,
public class Main1 {
public static void main(String[] args){
System.out.println("main bearst");
Thread t =new Thread();
t.start();
System.out.println("end");
}
class thread extends Thread{
@Override
public void run(){
System.out.println("hello word");
System.out.println("beast");
}
}
}
可以发现,在这个情况下,系统不会输出下面新线程中的东西,只会输出上面老线程中的信息,那么考虑到可能是在不同类当中可能存在时间的差距,那么将其写在同一个类当中是不是可以减少时间的损耗,作为验证,可以用新的代码做实验(当然不是这个原因,实际原因还是不可以直接调用的结果)
直接调用run()
方法,相当于调用了一个普通的Java方法,当前线程并没有任何改变,也不会启动新线程。上述代码实际上是在main()
方法内部又调用了run()
方法,打印hello
语句是在main
线程中执行的,没有任何新线程被创建。
必须调用Thread
实例的start()
方法才能启动新线程,如果我们查看Thread
类的源代码,会看到start()
方法内部调用了一个private native void start0()
方法,native
修饰符表示这个方法是由JVM虚拟机内部的C代码实现的,不是由Java代码实现的
public class Main1 {
public static void main(String[] args) {
System.out.println("main start...");
Thread t = new Thread() {
public void run() {
System.out.println("thread run...");
System.out.println("thread end.");
}
};
t.start();
System.out.println("main end...");
}
}
发现在这里打印出来了新的线程中的信息
main start...
main end...
thread run...
thread end.
这里得到新的信息但也要注意到其中的顺序和想要的不太一样,所以线程要知道两个重要的点,第一,同一线先后进行,第二,没有规定的情况下,不同线程同时进行
如果要实现线程中的一个线程一个线程的运行,那么最好在调用的时候实现对一个线程调用的时候,使用sleep对线程进行暂停,
public class Main {
public static void main(String[] args) {
System.out.println("main start...");
Thread t = new Thread() {
public void run() {
System.out.println("thread run...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("thread end.");
}
};
t.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
System.out.println("main end...");
}
}
要注意,这里再创建成果之后并没有直接启动所以在run之后还是在自己的线程中,而不是在新线程当中,所以不会进入run ,而是应该进行继续往下运行在遇到t.start()之后才会进入新线程。在进入之后会先输出thread run...之后会进入暂停,而在主线程当中启动后就暂停,但注意暂停时间不一定是和先后相同关系。毕竟程序在运行的时间也要算上,那么就很简单理解为啥在后面main会先进入end而新的线程会更晚的结束。
当然也可以在线程里面设置优先级,但要想清楚,这个并不一定会让高优先级的线程更早的调用,它只会更改调用的优先级,所以对调用次数少的不要设置太高的优先级,可能会导致时间上的浪费
Thread.setPriority(int n) // 1~10, 默认值5
这个就是更改优先级的办法
线程的状态
在写一个新的线程的时候,要注意线程对象是实现和start一对一的调用。在新线程中执行run
那么在调用run之后会存在更多的情况
- New:新创建的线程,尚未执行;
- Runnable:运行中的线程,正在执行
run()
方法的Java代码; - Blocked:运行中的线程,因为某些操作被阻塞而挂起;
- Waiting:运行中的线程,因为某些操作在等待中;
- Timed Waiting:运行中的线程,因为执行
sleep()
方法正在计时等待; - Terminated:线程已终止,因为
run()
方法执行完毕。d
但在我目前的水平来说,基本上Waiting和Timed Waiting 相差不大,几乎处于同义词的位置,但在任何情况下,线程一定要在最后触及Terminated,以确保程序会进行到结束,不然可能会一直占用cpu资源,导致后面程序难以正常运行
这里不存在太多的知识点,唯一可以说值得注意的是其在调用join()之后可以等待其结束,例如,main
线程在启动t
线程后,可以通过t.join()
等待t
线程结束后再继续运行