由于线程是依赖于进程的,所以我们先从进程开始。
1.1 进程
进程:是正在运行的程序
● 是系统进行资源分配和调用的独立单位
● 每一个进程都有它自己的内存空间和系统资源
1.2 线程
线程:是进程中的单个顺序控制流,是一条执行路径
●单线程:一个进程如果只有一条执行路径,则称为单线程程序
●多线程:—个进程如果有多条执行路径,则称为多线程程序
1.3 多线程的实现方式
Class Thread
public class Thread extends Object implements Runnable
线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。
创建一个新的执行线程有两种方法。
一个是将一个类声明为Thread
的子类。 这个子类应该重写Thread类的方法 run
。 然后可以分配并启动子类的实例。
(方式1:继承Thread类
①定义一个类MyThread继承Thread类
②在MyThread类中重写run0方法
③创建MyThread类的对象
④启动线程
)
-
例如,计算大于规定值的素数的线程可以写成如下:
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
-
PrimeThread p = new PrimeThread(143); p.start();
另一种方法来创建一个线程是声明实现类
Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。 这种其他风格的同一个例子如下所示: -
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
-
然后,以下代码将创建一个线程并启动它运行:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将null
参数传递给null
中的构造函数或方法将导致抛出NullPointerException
。
注意:
我们在创建完线程对象后,若调用run()方法,就实现的是单线程,其实run()方法的调用就没有启动线程。
我们应该调用start()方法:
两个小问题:
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用start():启动线程;然后由JVM调用此线程的run()方法
1.4 设置和获取线程名称
Thread类中设置和获取线程名称的方法
在启动多个线程后,我们并分不清是哪个线程,所以我们要想办法显示他的线程名称。
在不设置名称直接使用getName()方法时:
@Override
public void run(){
for (int i=0;i<10;i++){
System.out.println(getName()+":"+i);
}
}
运行结果:
Thread-0、1是默认名称
为什么是这样?我们跟进Thread一探究竟
我找到了一些和线程默认名称相关的源代码:
private String name; //无参构造方法 public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private Thread(...){ this.name = name;//当前Thread类有name的变量 } private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++;//后加加 }
getName()的工作:
就是返回Thread-这样的名称
现在我们设置线程名称:
我们使用带参构造方法:
我们自己定义的MyThead类并没有带参构造方法,如下:
public class MyThread extends Thread{
public MyThread(){
}
public MyThread(String name){
super(name);//访问父类Thread的带参构造方法
}
@Override
public void run(){
for (int i=0;i<10;i++){
System.out.println(getName()+":"+i);
}
}
}
现在我们就可以使用这样的语句:
MyThread my1 = new MyThread("线程1");
MyThread my2 = new MyThread("线程2");
my1.start();
my2.start();
我们还可以使用这样的一个静态方法:(获取main()方法所在的线程名称)
我们可以找出main方法是在一个main的名称线程中执行的
System.out.println(Thread.currentThread().getName());
1.5 线程调度
线程有两种调度模型
①分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
②抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法
public final int getPriority(): 返回此线程的优先级
public final void setPriority(int newPriority):更改此线程的优先级
java的默认优先级为5
查看优先级(最大、最小、默认):
线程的优先级高仅仅表示它获取CPU的几率高,并不是优先级高的每次都跑在前面。
1.6 线程控制
1.sleep()方法
public class ThreadSleep extends Thread{ @Override public void run(){ for (int i=0;i<22;i++){ System.out.println(getName()+":"+i); try { Thread.sleep(1000);//1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
每执行一次,线程休眠1秒,执行结果规整了很多。
2.join()方法
比如:只有皇帝驾崩了继承人才可以枪皇位
public class ThreadJoinDemo { public static void main(String[] args) { ThreadSleep ts1=new ThreadSleep(); ThreadSleep ts2=new ThreadSleep(); ThreadSleep ts3=new ThreadSleep(); ts1.setName("皇帝"); ts2.setName("继承者1"); ts3.setName("继承者2"); ts1.start(); try { ts1.join(); } catch (InterruptedException e) { e.printStackTrace(); } ts2.start(); ts3.start(); } }
只有ts1终止了,ts2和ts3才可以开始。
3.setDaemon()方法
我们为主线程(刘备)设置两个守护线程
当主线程挂掉以后,守护线程没有立即挂掉。
1.7 线程的生命周期
2.1 多线程的实现方式2
实现Runnable接口的方式
步骤:
MyRunnable.java:
public class MyRunnable implements Runnable{
//该类继承Runnable,而不是Thread,没有getName()方法
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
MyRunnableDemo.java:
public class MyRunnableDemo {
public static void main(String[] args) {
MyRunnable my=new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数
// Thread t1=new Thread(my);
// Thread t2=new Thread(my);
Thread t1=new Thread(my,"线程名字1");
Thread t2=new Thread(my,"线程名字2");
t1.start();
t2.start();
}
}