java - 013 - 进程与线程

系统资源

内存、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秒执行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 一、进程和线程 进程 进程就是一个执行中的程序实例,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。...
    阿敏其人阅读 2,611评论 0 13
  • 1.解决信号量丢失和假唤醒 public class MyWaitNotify3{ MonitorObject m...
    Q罗阅读 873评论 0 1
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 2,076评论 0 14
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,444评论 1 15
  • 静-美好人们想在城市中抓不到的东西而乡下这~随处可见。时常想着去大树下歇息;时常想着去河边扔扔石子激起一片片波澜;...
    简beautiful阅读 205评论 0 1