您的当前位置:首页java - 013 - 进程与线程

java - 013 - 进程与线程

2024-12-14 来源:哗拓教育

系统资源

内存、cup、总线

进程

运行中的程序,该程序对拥有的系统资源的逻辑描述。

多进程

操作系统可以同时运行多个程序。

线程

实际存在的,执行任务的一个顺序功能流,本身是不具有系统资源的,只能使用分配给程序的系统资源。

多线程

同一个进程中,多个功能流同时执行。
目的:最大限度使用CPU资源

  • 线程的2种启动方式
    1.继承Thread类,重写run方法
public class test {
   public static void main(String[] args) throws Exception {
        new MyThread().start();//启动线程
   }
}
class MyThread extends Thread{
     public void run(){
        System.out.println("MyThread Run");
   }
}

2.实现Runnable接口

public class test {
     public static void main(String[] args) throws Exception {
          new Thread(new PrimeRun()).start();
     }
}
 class PrimeRun implements Runnable{
     public void run(){
        System.out.println("PrimeRun");
   }
}

线程生命周期

简单生命周期图 完整的生命周期图.png

简述上面的类
1.多个线程都想持有一个对象的同步锁的时候,只能有一个线程会持有,其他线程将在该对象的锁池中等待,拿到锁后,又回到就绪状态,等待被cpu调度
2.当对象在同步线程中调用wait()方法,该线程就会在该对象的等待池中等待被唤醒,唤醒后又去锁池中,为获得同步锁,后续和上面一样

1.创建状态
新建的一个Runnable对象

2.就绪
进入调度池中,等待系统调度

3.阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。退出阻塞状态,会重新进入就绪状态

4.运行状态
获得cpu时间片,执行run方法
①、线程调用sleep方法主动放弃所占用的系统资源

②、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
③、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
④、线程在等待某个通知(notify)
⑤、程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法

5.销亡状态
run()方法执行完成,某些强制退出手段。死亡了不可再调用start()方法,可能Thread对象对象还存在内存中。

线程睡眠 - sleep

该方法是一个静态方法,作用是将当前线程睡眠。并不会释放线程锁持有的锁

new Thread(new Runnable(){
            public void run(){
                for (int i=0;i<100;i++){
                    System.out.println("current Thread Sleep:"+i);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }   
        }).start();

打印间隔为1秒,使用sleep方法生命周期状态变化如下
运行 -> 阻塞 -> 就绪(等待cpu调度)
这个调度是系统控制,所以上面说的打印间隔1秒,是有误差的,应该是大于1秒

线程让步 - yield

该方法也是静态方法,作用是让当前线程从运行状态进入就绪状态

class MyThread extends Thread {  
    public MyThread(String name, int pro) {  
        super(name);// 设置线程的名称  
        this.setPriority(pro);// 设置优先级  
    }  
    public void run() {  
        for (int i = 0; i < 20; i++) {  
            System.out.println("线程优先级"+this.getPriority()+this.getName() + "线程第" + i + "次执行!");  
            if (i % 5 == 0)  
                Thread.yield();  //一个线程最多执行5次,但是完全有可能进入就绪状态,马上又获得cpu调度(这个是不可控的)
        }  
    }  
} 
        test
        new MyThread("低级", 1).start();  
        new MyThread("中级", 5).start();  
        new MyThread("高级", 10).start(); 
结果:
线程优先级1低级线程第0次执行!
线程优先级10高级线程第0次执行!
线程优先级5中级线程第0次执行!
线程优先级10高级线程第1次执行!
线程优先级1低级线程第1次执行!
线程优先级1低级线程第2次执行!
线程优先级1低级线程第3次执行!
线程优先级1低级线程第4次执行!
线程优先级1低级线程第5次执行!
线程优先级10高级线程第2次执行!
线程优先级10高级线程第3次执行!
线程优先级10高级线程第4次执行!
线程优先级10高级线程第5次执行!
线程优先级5中级线程第1次执行!
线程优先级10高级线程第6次执行!
线程优先级5中级线程第2次执行!
线程优先级5中级线程第3次执行!
线程优先级1低级线程第6次执行!
线程优先级1低级线程第7次执行!
线程优先级5中级线程第4次执行!
线程优先级5中级线程第5次执行!
线程优先级10高级线程第7次执行!
线程优先级5中级线程第6次执行!
线程优先级5中级线程第7次执行!
线程优先级1低级线程第8次执行!
线程优先级5中级线程第8次执行!
线程优先级5中级线程第9次执行!
线程优先级10高级线程第8次执行!
线程优先级5中级线程第10次执行!
线程优先级1低级线程第9次执行!
线程优先级5中级线程第11次执行!
线程优先级5中级线程第12次执行!
线程优先级5中级线程第13次执行!
线程优先级10高级线程第9次执行!
线程优先级10高级线程第10次执行!
线程优先级10高级线程第11次执行!
线程优先级10高级线程第12次执行!
线程优先级10高级线程第13次执行!
线程优先级10高级线程第14次执行!
线程优先级10高级线程第15次执行!
线程优先级5中级线程第14次执行!
线程优先级1低级线程第10次执行!
线程优先级1低级线程第11次执行!
线程优先级1低级线程第12次执行!
线程优先级5中级线程第15次执行!
线程优先级10高级线程第16次执行!
线程优先级5中级线程第16次执行!
线程优先级5中级线程第17次执行!
线程优先级1低级线程第13次执行!
线程优先级5中级线程第18次执行!
线程优先级10高级线程第17次执行!
线程优先级10高级线程第18次执行!
线程优先级5中级线程第19次执行!
线程优先级1低级线程第14次执行!
线程优先级1低级线程第15次执行!
线程优先级10高级线程第19次执行!
线程优先级1低级线程第16次执行!
线程优先级1低级线程第17次执行!
线程优先级1低级线程第18次执行!
线程优先级1低级线程第19次执行! 

线程合并 - join

一个对象方法,类似线程依赖,一个线程必须依赖另一个线程执行完成后才可执行。
通俗的说:我加入了你,你该礼貌的让我先执行

class MyThread extends Thread {  
    public Thread t;
    public MyThread(String name) {  
        super(name);// 设置线程的名称  
    }  
    public void run() {  
        if (this.t != null){
            try {
                this.t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } 
        for (int i = 0; i < 20; i++) {  
            System.out.println(this.getName() + "线程第" + i + "次执行!");  
        }  
    }  
} 
test:
    MyThread t1 =   new MyThread("join线程");
    MyThread t2 =      new MyThread("被join线程");
    t2.t = t1;
    t1.start();
    t2.start();
执行结果:
join线程线程第0次执行!
join线程线程第1次执行!
join线程线程第2次执行!
join线程线程第3次执行!
join线程线程第4次执行!
join线程线程第5次执行!
join线程线程第6次执行!
join线程线程第7次执行!
join线程线程第8次执行!
join线程线程第9次执行!
join线程线程第10次执行!
join线程线程第11次执行!
join线程线程第12次执行!
join线程线程第13次执行!
join线程线程第14次执行!
join线程线程第15次执行!
join线程线程第16次执行!
join线程线程第17次执行!
join线程线程第18次执行!
join线程线程第19次执行!
被join线程线程第0次执行!
被join线程线程第1次执行!
被join线程线程第2次执行!
被join线程线程第3次执行!
被join线程线程第4次执行!
被join线程线程第5次执行!
被join线程线程第6次执行!
被join线程线程第7次执行!
被join线程线程第8次执行!
被join线程线程第9次执行!
被join线程线程第10次执行!
被join线程线程第11次执行!
被join线程线程第12次执行!
被join线程线程第13次执行!
被join线程线程第14次执行!
被join线程线程第15次执行!
被join线程线程第16次执行!
被join线程线程第17次执行!
被join线程线程第18次执行!
被join线程线程第19次执行!

线程优先级

线程优先级高的只能保证,该线程获得系统资源调度的概率高一些,这个优先级只能描述一个概率,所以是不能以此属性作为线程调度顺序标准的,优先级低的也并非没有机会执行

class MyThread extends Thread {  
    public MyThread(String name,int priority) {  
        super(name);// 设置线程的名称 
        this.setPriority(priority);
    }  
    public void run() {  
        for (int i = 0; i < 20; i++) {  
            System.out.println(this.getName() + "线程第" + i + "次执行!");  
        }  
    }  
}  
test:
    MyThread t1 =   new MyThread("1级",1);
    MyThread t2 =  new MyThread("5级",5);
    MyThread t3 =  new MyThread("10级",10);
    t1.start();
    t2.start();
    t3.start();
输出结果:
1级线程第0次执行!
1级线程第1次执行!
1级线程第2次执行!
10级线程第0次执行!
10级线程第1次执行!
10级线程第2次执行!
10级线程第3次执行!
10级线程第4次执行!
10级线程第5次执行!
10级线程第6次执行!
10级线程第7次执行!
10级线程第8次执行!
10级线程第9次执行!
10级线程第10次执行!
10级线程第11次执行!
10级线程第12次执行!
10级线程第13次执行!
5级线程第0次执行!
5级线程第1次执行!
5级线程第2次执行!
5级线程第3次执行!
5级线程第4次执行!
5级线程第5次执行!
5级线程第6次执行!
5级线程第7次执行!
5级线程第8次执行!
5级线程第9次执行!
5级线程第10次执行!
5级线程第11次执行!
5级线程第12次执行!
5级线程第13次执行!
5级线程第14次执行!
5级线程第15次执行!
5级线程第16次执行!
5级线程第17次执行!
5级线程第18次执行!
5级线程第19次执行!
10级线程第14次执行!
10级线程第15次执行!
10级线程第16次执行!
10级线程第17次执行!
10级线程第18次执行!
1级线程第3次执行!
10级线程第19次执行!
1级线程第4次执行!
1级线程第5次执行!
1级线程第6次执行!
1级线程第7次执行!
1级线程第8次执行!
1级线程第9次执行!
1级线程第10次执行!
1级线程第11次执行!
1级线程第12次执行!
1级线程第13次执行!
1级线程第14次执行!
1级线程第15次执行!
1级线程第16次执行!
1级线程第17次执行!
1级线程第18次执行!
1级线程第19次执行!

守护线程

守护线程和普通线程使用上并没有什么区别,应用程序的退出不需要关心守护线程是否执行完成

 t2.setDaemon(true);//设置为守护线程,只能在创建状态设置该属性,否则抛出异常

守护线程是不需要关心退出时机的,JVM的垃圾回收机制就是一个守护线程,如果应用程序的所有线程都是守护线程JVM虚拟机就会退出

  Thread t2 =   new Thread(new Runnable(){
         public void run(){
             for (int i =0 ;i < 999999; i++){
                 System.out.println("守护线程:" + i);
             } 
         }
     });
     t2.setDaemon(true);//设置为守护线程
     t2.start();
     new Thread(new Runnable(){
         public void run(){
             for (int i =0 ;i <10; i++){
                 System.out.println("普通线程:" + i);
             }
         }
     }).start();
输出结果:
守护线程:0
守护线程:1
守护线程:2
守护线程:3
守护线程:4
守护线程:5
守护线程:6
守护线程:7
守护线程:8
守护线程:9
守护线程:10
守护线程:11
守护线程:12
守护线程:13
守护线程:14
守护线程:15
守护线程:16
守护线程:17
守护线程:18
守护线程:19
普通线程:0
普通线程:1
普通线程:2
普通线程:3
普通线程:4
普通线程:5
普通线程:6
普通线程:7
普通线程:8
普通线程:9
守护线程:20

线程结束的常用方法

//线程结束的常用方法
     new Thread(new Runnable(){
         public void run(){
             for (int i =0 ;i <10; i++){
                if (i == 8) break;//不管用return还是一个boolean控制也好,只要将run方法执行完成即可
                 System.out.println("普通线程:" + i);
             }
         }
     }).start();

上面的结束一个线程的方法的先决条件就是该线程处于运行时起,当线程处于阻塞期,那么就无法达到退出的效果,就只能巧借线程的interrupt方法,该方法会将处理异常

class MyThread extends Thread{
    public void run(){
        for (int i =0 ;i <10 ; i++){
            try {
                System.out.println("-----"+i);
                Thread.sleep(5000);             
            } catch (InterruptedException e) {
                System.out.println("中断异常");
                return;
            }
        }
    }
public static void main(String[] args) throws Exception {
        MyThread t =    new MyThread();
        t.start();
        Thread.sleep(8000);
        t.interrupt();
    }
输出结果:
-----0
-----1
中断异常

线程同步

warning:多线程最大的问题就是对同一资源的处理
java中的每个对象都有一个同步锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示该对象上锁,此时其他任何线程都无法再去访问该对象的synchronized方法了,直到那个线程执行完毕或者抛出异常,那么僵该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。

获得同步锁的对象为this,
public void show() {  
    synchronized(this){ 
......
    }  
}
public synchronized void show() { 
...这种方式是上面那种方式的简写。
}  

静态方法同步(锁住的是当前类所对应的class对象)

public static synchronized void show() {  
    .... 
}

等同于

public static void show() {  
   synchronized(当前类名.class)   
}  

经典案例:
我和媳妇同时去银行取钱。

public class test {
    public static void main(String[] args) throws Exception {
        
        Account a = new Account(3000);
        
        Thread t1 = new Thread(new GetMoneyRun(a));
        Thread t2 = new Thread(new GetMoneyRun(a));
        
        t1.start();
        t2.start();
        

    }
}

class Account {
    int money;

    Account(int money) {
        this.money = money;
    }
    
    public  void getMoney(int a){
        this.money -= a;
    }
}

class GetMoneyRun implements Runnable {
    Account a;

    GetMoneyRun(Account a) {
        this.a = a;
    }

    public void run() {
            if (a.money > 2000){
                try {
                    Thread.sleep(1000);//这里就是为了模拟一下多线程同时操作同一资源
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                a.getMoney(2000);
                System.out.println("取现2000");
            }else{
                System.out.println("所剩余额不够");
            }   
        }
}
输出结果:
取现2000
取现2000

上面这种情况的解决方法就是:
1.对资源(对象)进行上锁

    public void run() {
        synchronized(a){
            if (a.money > 2000){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                a.getMoney(2000);
                System.out.println("取现2000");
            }else{
                System.out.println("所剩余额不够");
            }   
            
        }
    }
//输出结果:
取现2000
所剩余额不够

2.对方法进行上锁

    public synchronized  void getMoney(int a){
        if (money > a){
            money -= a;
            System.out.println("取现:"+a);
        }else{
            System.out.println("所剩余额不够");
        }   
    }
    
输出结果:
取现:2000
所剩余额不够

死锁

死锁造成的原因就是对资源的相互持有造成的

public class test {
    public static void main(String[] args) throws Exception {
       Object o1 = new Object();
       Object o2 = new Object();
        Thread t1 = new ChangeObject("线程1",o1,o2);
        Thread t2 = new ChangeObject("线程2",o2,o1);
        t1.start();
        t2.start();
    }
}
class ChangeObject extends Thread {
    Object o1;
    Object o2;
    ChangeObject(String threadName,Object o1, Object o2) {
        super(threadName);
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run() {
        synchronized(o1){
            try {
                System.out.println(this.getName() + " will sleep");
                Thread.sleep(1000);
                System.out.println(this.getName() + " did wake up");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            synchronized(o2){
                System.out.println("can you access");
            }
        }
    }
}

线程的协调运作

主要用java.lang.Object类中的方法wait()、notify()、notifyAll(),这三个方法必须由上锁的对象调用,所以一定是在同步方法中使用才有意义,调用这三个方法的调用线程必须持有该对象的锁(同步监视器),才可调用这三个方法,否则抛出异常

同步监视器

wait();//导致当前线程等待,并且释放该线程持有的同步锁。直到其他线程的该同步监视器唤醒(notify()、notifyAll())
notify();//唤醒该同步监视器上,等待的线程,多个的话就任选一个。只是被唤醒,需重新争取系统调度
notifyAll();//唤醒该同步监视器上等待的所有线程。
public static void main(String[] args) throws Exception {
    Person p = new Person();
    p.start();
    synchronized(p){
        System.out.println("do Action1");
        p.wait();
        System.out.println("do Action 4");
    }
    }
class Person extends Thread{
    public void run(){
        synchronized(this){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("do Action 2");
            this.notify();
            System.out.println("do Action 3");
        }
    }
}
输出结果:
do Action1
do Action 2
do Action 3
do Action 4

线程池

创建一个固定大小(并发大小)的线程池


public class test {
        public static void main(String[] args) throws Exception {
            ExecutorService service =       Executors.newFixedThreadPool(5);//创建一个固定大小的任务池
            for (int i = 0 ;i <9; i++){
                service.submit(new Mythread("线程"+ i));
            }
            service.shutdown();//该方法会等任务池都完成,才会关闭
    }
    }
    class Mythread  extends Thread{
        String name = "";
        Mythread(String name){
            this.name = name;
        }
        public void run(){
            System.out.println(this.name);
        }
    }

还有一些类型的线程池方法,如下:

ExecutorService singleService = Executors.newSingleThreadExecutor();//单线程池
Executors.newCachedThreadPool();  //可变线程池
Executors.newScheduledThreadPool(5);//延迟线程池
Executors.newSingleThreadScheduledExecutor(); //单任务延迟线程池

当某个线程加到线程池中,需要延迟执行的时候

pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);//延迟1秒执行
显示全文