进程的状态与生命周期
要想实现多线程就要在主线程中创建新的线程 新建的线程在他的一个完整的生命周期内通常要经历五种状态 通过线程的控制与调度可使线程在这几种状态之间转化
- 新建状态 : Thread 或者其子类的对象被声明并创建 但是还没有被执行的这段时间里面 处于一种特别的新建状态中 这时候线程对象已经被分配了内存空间和其他的资源 并且已经被初始化 但是该线程已经没有被调度 此时的线程可以被调度 变成了就绪状态
- 就绪状态 : 也可以叫可运行状态 处于新建状态的线程被启动后 将进入线程队列等待CPU资源 这时候他已经有了运行的条件 也就是处于了就绪状态 等到他来使用CPU资源的时候 就可以脱离主线程开始自己的生命周期
- 执行状态 : 当就绪状态的线程被调度并获得cpu 资源时候 就进入了执行状态 该状态表示线程正在执行 它拥有了cpu的控制权 每一个Thread类和他的子类对象都有一个重要的run()方法 该方法定义了这一类线程的操作和功能 当 线程被调度时候 他将自动的调用本对象的run()方法 从该方法的第一条开始运行一直到最执行完毕
除非是线程主动的让出cpu的控制权或者cpu的控制权被优先级更高的线程抢占 - 阻塞状态 :一个正在执行的线程由于某种特殊的情况 将让出cpu 并且中止自己的执行 线程处于这样的状态被称为阻塞状态
在这种状态下即使cpu空闲也不能执行线程 进入阻塞的方法:
调用sleep()或者yield()方法
为了等待一个条件变量 ,线程调用wait()方法
该线程和另一个线程join()在一起了
处于阻塞状态的线程通常会想要某些事件唤醒 - 消亡状态: 处于消亡状态的线程不具有继续执行的能力
消亡的原因有:
正常的运行的线程完成了全部的任务
进程因故停止后 线程被强行终止
线程消亡状态 并且没有线程对象引用后 垃圾回收器 会从内存里面删除线程对象
Thread线程类和Runnable接口
实现多线程的方法有 两个
一个是java.lang的Thread 类
一个是用户在定义自己的类中实现Runnable 接口
利用Thread 类的子类来创建线程
要在一个Thread的子类里面激活线程 需要注意 这个类必须继承Thread类 和线程要执行的代码必须写在run()里面
class 类名 extends Thread{
// 成员变量
//成员函数
//修饰符 +run()
{
// 线程的代码
}
}
利用Thread 类的子类创建线程
public class myThread extends Thread {
private String who;
public myThread(String str){
who = str;
}
public void run(){
for (int i=0 ;i<5;i++)
{
try {
sleep((int)(1000*Math.random()));
}catch (InterruptedException e){
}
System.out.println(who+"正在运行!");
}
}
public static void main(String[] args) {
myThread you = new myThread("你");
myThread she = new myThread("她");
you.start(); // 调用的不是run()哦
she.start();
System.out.println("主方法main()运行结束");
}
}
}
结果:
主方法main()运行结束
你正在运行!
你正在运行!
她正在运行!
你正在运行!
她正在运行!
你正在运行!
她正在运行!
你正在运行!
她正在运行!
她正在运行!
main 也是一个线程 执行完成you.start()和she.start()后继执行
至于 先输出运行结束还是进入start() 全部看是谁抢到了cpu资源
(本人测试一直是运行先出来 刚开始一直是以为输出运行结束的那一句先出来 后来才知道最后的输出运行结束不需要线程激活的过程 所以一般先运行)
用Runnable接口来创建线程
Thread 直接继承了Object 类 实现了Runnable接口 所以他的子类都有线程功能 所以用户可以直接声明一个类并且实现Runnable接口
并且定义run()方法 但是Runnable的接口没有任何的对线程支持 所以还必须创建一个Thread的实例
Runnable接口的好处不仅在于它间接的解决的多重继承的问题 和Thread相比 Runnable接口更加的适合于多个线程解决同一资源
在这里插入代码片
public class myThread2 implements Runnable{
private String who;
public myThread2(String str){
who = str;
}
@Override
public void run() {
for (int i=0 ;i<5;i++)
{
try {
Thread.sleep((int)(1000*Math.random()));
}catch (InterruptedException e){
System.out.println(e.toString());
}
System.out.println(who+"正在运行!");
}
}
public static void main(String[] args) {
myThread2 you = new myThread2("你");
myThread2 she = new myThread2("她");
Thread t1 = new Thread(you);
Thread t2 = new Thread(she);
t1.start();
t2.start();
}
}
该代码的功能和上面的基本相同
如果需要有序的运行
需要使用Thread中的join()的方法
当某一个线程调用join()方法的时候 其他的线程需要等待她运行结束才会开始执行
在这里插入代码片
public class myThread extends Thread {
private String who;
public myThread(String str){
who = str;
}
public void run(){
for (int i=0 ;i<5;i++)
{
try {
sleep((int)(1000*Math.random()));
}catch (InterruptedException e){
}
System.out.println(who+"正在运行!");
}
}
public static void main(String[] args) {
myThread you = new myThread("你");
myThread she = new myThread("她");
you.start(); // 调用的不是run()哦
try {
you.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
she.start();
try {
she.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主方法main()运行结束");
}
}
结果
你正在运行!
你正在运行!
你正在运行!
你正在运行!
你正在运行!
她正在运行!
她正在运行!
她正在运行!
她正在运行!
她正在运行!
主方法main()运行结束
通过继承Thread 类的优点是编写简单 直接的操作线程 缺点是继承了Thread 就不能继承其他类
线程之间的数据共享
例如
有3个售票窗口 共有5个票 出售火车票
public class Threadsale extends Thread {
private int tickets = 5;
@Override
public void run() {
while (true){
if (tickets>0){
System.out.println(this.getName()+"售票机"+tickets--+"号票");
}else{
System.exit(0);
}
}
}
public static void main(String[] args) {
Threadsale t1 = new Threadsale();
Threadsale t2 = new Threadsale();
Threadsale t3 = new Threadsale();
t1.start();
t2.start();
t3.start();
}
}
结果
Thread-1售票机5号票
Thread-2售票机5号票
Thread-2售票机4号票
Thread-2售票机3号票
Thread-2售票机2号票
Thread-2售票机1号票
Thread-0售票机5号票
Thread-0售票机4号票
Thread-0售票机3号票
Thread-0售票机2号票
Thread-0售票机1号票
Thread-1售票机4号票
Thread-1售票机3号票
Thread-1售票机2号票
Thread-1售票机1号票
你会发现一张票同时出售了3次 着明显是不合理的
创建的3个线程都有自己的资源 他们之间没有共享资源
用Runnable接口来完成
在这里插入代码片
public class Threadsale extends Thread {
private int tickets = 5;
@Override
public void run() {
while (true){
if (tickets>0){
System.out.println(Thread.currentThread().getName()+"售票机"+tickets--+"号票");
}else{
System.exit(0);
}
}
}
public static void main(String[] args) {
/* Threadsale t1 = new Threadsale();
Threadsale t2 = new Threadsale();
Threadsale t3 = new Threadsale();
t1.start();
t2.start();
t3.start(); */
Threadsale t = new Threadsale();
Thread t1 = new Thread(t,"第一个窗口");
Thread t2 = new Thread(t,"第二个窗口");
Thread t3 = new Thread(t,"第三个窗口");
t1.start();
t2.start();
t3.start();
}
}
结果:
第一个窗口售票机4号票
第二个窗口售票机3号票
第三个窗口售票机5号票
第二个窗口售票机1号票
第一个窗口售票机2号票