Java学习笔记-进程与线程

一:进程与线程

1:操作系统与进程

1:进程:进程是一个程序从加载代码,执行代码再到代码执行结束为止的一次动态执行的过程,操作系统控制每个进程的开始与结束。可以同时控制多个进程轮流访问cpu。

2:进程与线程

线程是比进程更小的单位,在一个进程执行的过程中可以产生多个线程,每个线程之家金可以共享资源。

3:Java中线程的状态与生命周期

Java语言使用Thread类或者其子类的对象来表示线程,完整的线程生命周期包含以下几个方面:
1:新建
当类Thread或者他的子类的对象被创建的时候,新建的线程对象就处于新建状态,此时它有了相应的内存空间和资源,但没有获得cpu的使用权。
2:运行
新建的对象拿到cpu的使用权时,就进入了次线程的运行状态。创建后的线程还需要调用start()方法告诉JVM有一个线程正在排队等候运行。拿到cpu使用权后,线程会调用run()方法立刻实现该线程要完成的任务。run()方法必须重写。
3:中断
四种中断原因:

  1. JVM将cpu资源从一个线程切换到另一个线程,就会使得次线程因没有cpu使用权处于中断状态。
  2. 线程在使用cpu资源期间,执行了sleep(int millsecond)方法,使得当前先线程进入了休眠状态,sleep(int millsecond)方法是Thread类的一个类方法,执行此方法后,线程立刻交出cpu的使用权,进入中断,等待时间millsecond毫秒后,从新进入排队序列。
  3. 线程在执行期间,使用了wait()方法,使得当前线程进入等待状态,等待状态的线程不会主动进入排队序列,而是由其他线程通过方法notify()通知。
  4. 线程执行期间会由于某个操作而进入阻塞状态,此时处于阻塞状态的线程不能进入排队序列,必须等待阻塞原因结束,才能再次进入线程排序队列。

4:死亡
所谓的死亡就是线程被释放了实体,即释放了分配给线程的内存。原因如下:

  1. 执行完了run方法的所有内容即正常执行完。
  2. 被线程强制停止释放。
    例如:
    Test_main.java
public class Test_main {
    public static void main(String[] args) {
        PrintElephant printElephant=new PrintElephant();   //创建打印大象的线程
        PrintCar printCar=new PrintCar();        //创建打印汽车的线程
        printElephant.start();               //打印大象线程开始
        printCar.start();                     //打印汽车线程开始
        for (int i = 0; i < 15; i++) {          //主线程开始
            System.out.print("主人"+i+" ");
        }
    }
}

PrintElephant.java

public class PrintElephant extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.print("大象"+i+" ");
        }
    }
}

PrintCar.java

public class PrintCar extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.print("汽车"+i+" ");
        }
    }
}

运行结果:

大象0 主人0 主人1 主人2 主人3 主人4 主人5 汽车0 主人6 大象1 主人7 汽车1 主人8 汽车2 大象2 
汽车3 主人9 主人10 主人11 主人12 主人13 主人14 汽车4 汽车5 汽车6 汽车7 汽车8 汽车9 汽车10 
汽车11 汽车12 汽车13 汽车14 汽车15 汽车16 汽车17 汽车18 大象3 大象4 汽车19 大象5 大象6 大象7 
大象8 大象9 大象10 大象11 大象12 大象13 大象14 大象15 大象16 大象17 大象18 大象19 
进程已结束,退出代码为 0

每次结果均不一样,是因为三个线程轮流执行的顺序是不定的。

4:线程调度与优先级

Java虚拟机通过线程调度器管理线程,调度器把线程等级分为10个等级(1-10)即Thread.MIN_PRIORITYThread.MAX_PRIORITY,调度器调用方法setPoriority(int grade)来设置线程的优先级。

二:Thread类与线程的创建

1:使用Thread的子类、

使用子类创建线程是需要重写run方法。也可以在子类中增加新的成员变量,是线程具有某种属性,也可以添加新的方法,是线程具有某种功能。

2:使用Thread类

构造方法为:Thread(Runnable target),其中Runnable是一个接口,因此必须写一个类用来实现接口Runnable,并且用词类的对象传递给Thread,run方法在该类中实现。
对于同一目标对象的线程,目标对象的成员变量就是共享的成员变量,例如下面的例子,有两个线程一个是狗狗喝水,一个数猫猫喝水,他么的公告目标对象时house,house有成员变量water,被二者共享。
运行结果:

Test_main.java

public class Test_main {
    public static void main(String[] args) {
        House house=new House(); //目标对象
        house.setWaterAmount(10);
        Thread cat,dog;    //使用Thread类来创建线程
        dog=new Thread(house);  //二者共享一个目标对象
        cat=new Thread(house);
        dog.setName("狗狗");
        cat.setName("猫猫");
        dog.start();
        cat.start();
    }
}

House.java

public class House implements Runnable{     //实现了接口Runnable
    int WaterAmount;   //共享的成员变量  

    public void setWaterAmount(int waterAmount) {
        WaterAmount = waterAmount;
    }

    @Override
    public void run() {    //重写run方法
        while (true)
        {
            String name=Thread.currentThread().getName();
            if(name.equals("狗狗"))
            {
                System.out.println(name+"喝水");
                WaterAmount-=2;
            }
            else if(name.equals("猫猫"))
            {
                System.out.println(name+"喝水");
                WaterAmount-=1;
            }
            System.out.println("剩"+WaterAmount);
            try{
                Thread.sleep(2000);
            }catch (InterruptedException e){}
            if(WaterAmount<=0)
            {
                return;
            }
        }
    }
}

狗狗喝水
猫猫喝水
剩87
猫猫喝水
剩6
狗狗喝水
剩4
猫猫喝水
狗狗喝水
剩31
猫猫喝水
剩0
狗狗喝水
剩-2

进程已结束,退出代码为 0

3:目标对象与线程的关系

1:目标对象与线程完全解耦
目标对象不包含对线程对象的引用时,称为解耦,这种情况下通过获得线程对象的名字来获得那个线程正在占用CPU资源。

String name =Thread.currentThread().getName()

2:目标对象组合线程(弱耦合)
目标对象可以组合线程,即将线程作为自己的成员,比如上面的将cat和dog在house中,此时要通过Thread.currentThread()来确定那个线程在占用CPU资源。
如果将上面的例子改为将dog与cat的线程对象放在house里面时,代码修改如下:
Test_main.java

public class Test_main {
    public static void main(String[] args) {
        House house=new House(); //目标对象
        house.setWaterAmount(10);
        house.dog.start();
        house.cat.start();

    }
}

House.java

public class House implements Runnable{     //实现了接口Runnable
    int WaterAmount;   //共享的成员变量
    Thread cat,dog;    //使用Thread类来创建线程
    House(){
        dog=new Thread(this);
        cat=new Thread(this);
    }
    public void setWaterAmount(int waterAmount) {
        WaterAmount = waterAmount;
    }

    @Override
    public void run() {    //重写run方法
        while (true)
        {
            Thread t=Thread.currentThread();  //通过引用来判断
            if(t==dog)       
            {
                System.out.println("狗狗喝水");
                WaterAmount-=2;
            }
            else if(t==cat)
            {
                System.out.println("猫猫喝水");
                WaterAmount-=1;
            }
            System.out.println("剩"+WaterAmount);
            try{
                Thread.sleep(2000);
            }catch (InterruptedException e){}
            if(WaterAmount<=0)
            {
                return;
            }
        }
    }
}

三: 线程中的常用方法

1start() /*调用该方法启动线程,使一个线程从新建状态转变为就绪排队状态,当他获得CPU资源后,就
开始自己的生命周期。*/
2run() /*定义线程对象被调度之后所执行的操作,run方法执行完成后,线程就变为死亡状态,即释放分配
给线程的资源。*/
3sleep(int millsecond) /*使某一个线程进入休眠状态,休眠时间有参数millsecond来决定一毫秒为单位
在try-catch方法中调用。*/
4isAlive() /*查看线程是否处于运行状态。*/
5currentThread() /*返回当前占用CPU的线程。*/
6interrupt() /*用来吵醒休眠的线程*/

例如:
Test_main.java

public class Test_main {
    public static void main(String[] args) {
        ClassRoom room6501=new ClassRoom();
        room6501.student.start();
        room6501.teacher.start();
    }
}

ClassRoom.java

public class ClassRoom implements Runnable{
    Thread student,teacher;
    ClassRoom()
    {
        teacher=new Thread(this);
        student=new Thread(this);
        teacher.setName("王老师");
        student.setName("李四");
    }
    @Override
    public void run() {
        if(Thread.currentThread()==student)
        {
            try {
                System.out.println(student.getName()+"正在睡觉,不听课");
                Thread.sleep(1000*60*60);    //睡觉时间为一个小时
            } catch (InterruptedException e) {
                System.out.println(student.getName()+"被老师叫醒了");
                e.printStackTrace();
            }
            System.out.println(student.getName()+"开始上课");
        }
        else if(Thread.currentThread()==teacher)
        {
            for (int i = 0; i < 3; i++) {
                System.out.println("上课");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            student.interrupt();  //睡觉未结束,被叫醒
            
        }

    }
}

四:线程同步

所谓的线程同步指的是当有多个线程同时使用同一个方法时,其他线程必须等待当前线程完成他的操作后,其他线程才可以调用。完成同步操作的关键字是:synchronized,次关键字修饰的方法为同步机制。下面例子中就有两个线程同时调用一个方法。如:
Test_main.java

public class Test_Main {
    public static void main(String[] args) {
        Bank bank=new Bank();
        bank.setMoney(500);
        Thread accountant,cashier;
        accountant=new Thread(bank);
        cashier=new Thread(bank);
        accountant.setName("会计");
        cashier.setName("出纳");
        accountant.start();
        cashier.start();
    }
}

Bank.java

public class Bank implements Runnable{
    int Money=200;
    public void setMoney(int n)
    {
        Money=n;
    }
    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("会计"))
        {
            saveOrTake(300);
        }
        else if(Thread.currentThread().getName().equals("出纳"));
        {
            saveOrTake(150);
        }

    }
    public synchronized void  saveOrTake(int n)
    {
        if(Thread.currentThread().getName().equals("会计"))
        {
            for (int i = 0; i <=3; i++) {
                Money=Money+n/3;
                System.out.println(Thread.currentThread().getName()+"存入"+n/3+",账上有"+Money+"万,休息一会");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        else if(Thread.currentThread().getName().equals("出纳"))
        {
            for (int i = 0; i <=3 ; i++) {
                Money=Money-n/3;
                System.out.println(Thread.currentThread().getName()+"取出"+n/3+",账上有"+Money+"万,休息一会");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

没有加关键字synchronized的运行结果

出纳取出50,账上有550万,休息一会
会计存入100,账上有550万,休息一会
出纳取出50,账上有500万,休息一会
会计存入100,账上有600万,休息一会
出纳取出50,账上有550万,休息一会
会计存入100,账上有650万,休息一会
出纳取出50,账上有600万,休息一会
会计存入100,账上有700万,休息一会
会计存入50,账上有750万,休息一会
会计存入50,账上有800万,休息一会
会计存入50,账上有850万,休息一会
会计存入50,账上有900万,休息一会

进程已结束,退出代码为 0

加了关键字synchronized的结果

会计存入100,账上有600万,休息一会
会计存入100,账上有700万,休息一会
会计存入100,账上有800万,休息一会
会计存入100,账上有900万,休息一会
会计存入50,账上有950万,休息一会
会计存入50,账上有1000万,休息一会
会计存入50,账上有1050万,休息一会
会计存入50,账上有1100万,休息一会
出纳取出50,账上有1050万,休息一会
出纳取出50,账上有1000万,休息一会
出纳取出50,账上有950万,休息一会
出纳取出50,账上有900万,休息一会

五:协调同步的线程

当一个线程使用的同步方法中用到某个变量,此变量在某个线程中使用时,必须对其线改变才能使用,这就会导致线程阻塞在这,此时,我们需要将次线程进行wait()操作,使其暂时交处CPU的控制权,让里一个线程改变这个变量后,调用notifyAll()方法通知刚才的线程进入排序队列,notify()与notifyAll()方法的区别在于,一个是通知所有wait的线程,一个只通知一个线程。
下面的例子中,张飞和李逵同时去电影院买票,张飞在李逵前面,电影票一张5元,没有零钱可照,张飞有一张20元,李逵是一张5元,因为非没有零钱,但张飞的线程在李逵的前面,如果张飞不等待整个程序就会一直阻塞,所以需要张飞先等待,让李逵先买票,等有零钱后,张飞才可以买票。

Test_main.java

public class Test_main {
    public static void main(String[] args) {
        TicketHouse officer=new TicketHouse();
        Thread zhangfei,likui;
        zhangfei=new Thread(officer);
        zhangfei.setName("张飞");
        likui=new Thread(officer);
        likui.setName("李逵");
        zhangfei.start();
        likui.start();
    }
}

TicketHouse.java

public class TicketHouse implements Runnable{
    int fiveAmount=2,tenAmount=0,twentyAmount=0;

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("张飞"))
        {
            saleTicket(20);

        }
        else if(Thread.currentThread().getName().equals("李逵"))
        {
            saleTicket(5);
        }
    }
    private synchronized void saleTicket(int money)
    {
        if(money==5)
        {
            fiveAmount+=1;
            System.out.println("给"+Thread.currentThread().getName()+"入场券,"+Thread.currentThread().getName()+"的钱正好");
        }
        else if(money==20)
        {
            while (fiveAmount<3)
            {
                try {
                    System.out.println("\n"+Thread.currentThread().getName()+"靠边等。。。");
                    wait();
                    System.out.println("\n"+Thread.currentThread().getName()+"继续买票");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                fiveAmount-=3;
                twentyAmount+=1;
                System.out.println("给"+Thread.currentThread().getName()+"入场券,"+Thread.currentThread().getName()+"给20,找15元");
            }
        }
        notifyAll();
    }
}

运行结果:

张飞靠边等。。。
给李逵入场券,李逵的钱正好

张飞继续买票
给张飞入场券,张飞给20,找15元

张飞靠边等。。。

进程已结束,退出代码为 -1

【注】:在许多的实际问题中,wait方法应该放在一个while的循环语句中,而不是if 的分支语句。

六:线程联合

一个线程可以调用方法:join()使得另一个线程与其联合,如果线程在运一个行过程中联合了另一个线程,那么此线程将会立刻被中断执行,一直到联合的线程执行完,它才可以重新排队,

七:GUI线程

当Java程序中出现图形化界面时,JVM会启动更多的线程,其中有两个重要的线程:
1:AWT-EventQuecue
2:AWT-Windows
其中第一个线程用来处理GUI事件,第二个用来将窗体和控件进行布局。、

八:计时器线程

Java提供的一个Timer类,在包java.swing中,注意在包java.util中也存在一个Timer类。注意区分.
构造方法为

1Timer(int a,Object b) 
其中参数a的单位是毫秒,每a毫秒后“响铃一次”,参数b是监视器,当响铃一次,器调用ActionListener接口
中的方法actionPorformed(ActionEvent e)。也就是每响铃一次,执行一次此方法。如果想只“响铃”一次,
可以调用方法setReapeats(boolean b),使其b值为false2Timer(int a)
这种构造方法计时器必须明显的调用ActionListener(ActionListener listener)方法获得监视器。

计时器创建后,调用start()方法开始计数,调用stop()方法挂起,调用restart()方法重新启动。
【注】:计时器的监视器必须是组件类的 实例。
例如:
Test_main.java

public class Test_main {
    public static void main(String[] args) {
        WindowTime win=new WindowTime();
        win.setTitle("计时器");
    }
}

WindowTime.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class WindowTime extends JFrame implements ActionListener {
    JTextField text;
    JButton bStart,bStop,bContinue;
    Timer timer;
    SimpleDateFormat m;
    int n=0,start=1;
    WindowTime()
    {
        timer=new Timer(1000,this);
        m=new SimpleDateFormat("hh:mm:ss");
        text=new JTextField(10);
        bStart=new JButton("开始");
        bStop=new JButton("停止");
        bContinue=new JButton("继续");
        bStart.addActionListener(this);
        bStop.addActionListener(this);
        bContinue.addActionListener(this);
        setLayout(new FlowLayout());
        add(bStart);
        add(bStop);
        add(bContinue);
        add(text);
        setSize(500,500);
        validate();
        setVisible(true);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==timer)
        {
            Date date=new Date();
            text.setText("时间"+m.format(date));
            int x=text.getBounds().x;
            int y=text.getBounds().y;
            y+=2;
            x-=2;
            text.setLocation(x,y);
        }
        else if(e.getSource()==bStart)
        {
            timer.start();
        }
        else if(e.getSource()==bStop)
        {
            timer.stop();
        }
        else if(e.getSource()==bContinue)
        {
            timer.restart();
        }
    }
}

九:守护线程

线程一般是非守护线程,线程可以调用方法void setDaemon(true)将其设置为守护线程,守护线程与普通线程的区别在于:当所有线程运行完后,守护线程也立刻结束运行,即使它的run方法还未运行完成。守护线程一般用于一些不是很严格的工作中。

十:小结

1:线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程。
2:JVM(Java虚拟机)负责管理线程。
3:线程创建后仅仅是占用了内存,排队序列里是没有这个线程的。当创建的线程调用start()方法时,线程才开始排。
4:线程同步是是指几个线程需要调用同一个同步方法,此方法需要用关键词synchroized()来修饰,必要的时候还需要使用wait()方法让当前线程“等待”,然后调用notify()函数进行释放。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值