java基础语法(三十四)线程和进程(一)

线程和进程的理解

1.什么是进程?什么是线程?
进程是一个应用程序(一个进程是一个软件)
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程.
2、对于java程序来说,当在DOS命令窗口中输入:
java HelloWorld 回车之后。
会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾.
最起码,现在的java程序中至少有两个线程并发,
一个是垃圾回收线程,一个是执行main方法的主线程。

3.进程和线程是什么关系?举个例子
进程可以看做是现实生活当中的公司。
线程可以看做是公司当中的某个员工。
注意:
进程A和进程B的内存独立不共享。
线程A和线程B呢?
在java语言中:
线程A和线程B,堆内存和方法区内存共享。
但是栈内存独立,一个线程一个栈.
假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
互不干扰,各自执行各自的,这就是多线程并发。
java中之所以有多线程机制,目的就是为了提高程序的处理效率。
4. 思考一个问题:
使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
压栈弹栈.
5.分析一个问题:对于单核的cPU来说,真的可以做到真正的多线程并发吗?
对于多核的CPU电脑来说,真正的多线程并发是没问题的.
4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。
什么是真正的多线程并发?
t1线程执行t1的。
t2线程执行t2的.
t1不会影响t2。t2也不会影响t1.这叫做真正的多线程并发。
单核的cpu不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发"的感觉。
对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
CPU的处理速度极快,多个线程之间频繁切换执行,给人的感觉是:多个事情同时在做!!!
线程的生命周期:
在这里插入图片描述

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法.
    //定义线程类
    public class MyThread extends Thread{
        public void run() {
        }
    }

    //创建线程对象
    MyThread t = new MyThread();
    //启动线程
    t.start();
第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法.
        //定义一个可运行的类。
        class MyRunnable implements Runnable{
            @Override
            public void run() {
            }
         }

         //创建线程对象
         Thread t=new Thread(new MyRunnable());
         //启动线程
         t.start();

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

6.关于线程对象的生命周期?
新建状态
就绪状态
运行状态
阻塞状态
死亡状态

实现线程的四种方式

实现线程的第一种方式:
编写一个类,直接继承java.lang.Thread ,重写run方法。

怎么创建线程对象? new就行了。
怎么启动线程呢?调用线程对象的start()方法。
public class ThreadTest02 {
    public static void main(String[] args) {
        //这里是main方法,这里的额代码属于主线程,在主栈中运行。
        //新建一个分支线程对象
        MyThread t = new MyThread();
        //启动线程
        //t.run();//不会启动线程,不会分配新的分支栈。(这种方式就是单线程)
        // start()方法的作用是:启动一个分支就程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
        //这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来, start()方法就結束了。线程就启动成功了。
        //启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
        // run方法在分支栈的栈底部。main方法在主栈的栈底部。run和main是平级的。
        t.start();
        //下面的代码还是在主线程中运行
        for (int i =0;i < 1000;i++){
            System.out.println("主线程--->"+i);
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        //编写程序,这段程序运行在分支线程中(分支栈)。
        for(int i = 0; i < 1000; i++){
            System.out.println("分支线程--->" + i);
        }

    }
}

上述代码start的内存图
在这里插入图片描述
上述代码run的内存图
在这里插入图片描述

实现线程的第二种方式。编写一个类实现java.lang.Runnable接口。

public class ThreadTest03 {
    public static void main(String[] args) {
        //创建一个可运行的对象
        //MyRunnable r =new MyRunnable();
        //将可运行的对象封装成一个线程对象
        //Thread t=new Thread(r);
        Thread t =new Thread(new MyRunnable());

        t.start();
        for (int i =0;i < 100;i++){
            System.out.println("主线程--->"+i);
        }
    }
}

//这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable{

    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

实现线程的第三种方式:采用匿名内部类

public class ThreadTest04 {
    public static void main(String[] args) {
        //创建线程对象,采用匿名内部类方式
        Thread t= new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 100; i++){
                    System.out.println("分支线程--->" + i);
                }
            }
        });
        t.start();
        for(int i = 0; i < 100; i++){
            System.out.println("主线程--->" + i);
        }
    }
}

实现线程的第四种方式
实现线程的第四种方式:实现callable接口。 ( JDK8新特性。)
这种方式实现的线程可以获取线程的返回值.
之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。
思考:
系统委派一个线程去执行一个任务,该线程执行完任务之后,可能 会有一个执行结果,怎么能拿到这个执行结果呢?使用第四种方式:实现callable接口方式.
这种方式的优点:可以获取到线程的执行结果。
这种方式的缺点:在获取t线程执行结果的时候,当前线程受阻塞,效率较低。

public class ThreadTest15 {
    public static void main(String[] args) throws Exception {
        //第一步:创建一个"未来任务类"对象
        //参数非常重要,需要给一个Callable接口实现类对象。

        FutureTask task=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {//call()方法相当于run方法,只不过这个有返回值
                //线程执行一个任务,执行完成之后会有一个执行结果
                //模拟执行
                System.out.println("call method begin!");
                Thread.sleep(1000*10);
                System.out.println("call method end!");
                int a=100;
                int b=200;
                return a+b;//自动装箱(300结果变成Integer)
            }
        });

        //创建线程对象
        Thread t=new Thread(task);
        //启动线程
        t.start();
        //这里是main方法,这是在主线程中。
        //在主线程中,怎么获取t线程的返回結果?
        //get()方法的执行会导致"当前线程阻塞"
        Object obj = task.get();
        System.out.println("线程执行结果:"+ obj);

        // main方法这里的程序要想执行必须等待get()方法的結束
        //而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
        //另一个线程执行是需要时间的。
        System.out.println("hello world!");
    }
}

线程对象的获取

1、怎么获取当前线程对象?
Thread t= Thread.currentThread();
返回值t就是当前线程。
2、获取线程对象的名字
String name=线程对象.getName();
3、修改线程对象的名宇
线程对象.setName(“线程名字”);
4、当线程没有设置名字的时候,默认的名字有什么规律?
Thread-0
Thread-1

public class ThreadTest05 {
    public static void main(String[] args) {
        //currentThread就是当前线程对象
        //这个代码出现在main方法当中,所以当前线程就是主线程
        Thread currentThread=Thread.currentThread();
        System.out.println(currentThread.getName());//main
        //创建线程对象
        MyThread2 t = new MyThread2();
        //设置线程的名字
        //t.setName("tt");
        //获取线程的名字
        String tName=t.getName();//线程默认的名字 Thread-0
        System.out.println(tName);

        MyThread2 t2=new MyThread2();
        System.out.println(t2.getName());//默认的名字是 Thread-1
        //启动线程
        t.start();
    }
}

class MyThread2 extends Thread{
    public void run(){
        for (int i=0;i<100;i++){
            System.out.println("分支线程--->"+i);
        }
    }
}

线程的睡眠

关于线程的sleep方法: sleep–进入阻塞状态
static void sleep(long millis)
1.静态方法: Thread.sleep(1000);
2.参数是毫秒
3.作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片.让给其它线程使用。
这行代码出现在A线程中, A线程就会进入休眠。
这行代码出现在B线程中, B线程就会进入休眠。
4. Thread.sleep()方法,可以做到这种效果:
间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。

public class ThreadTest06 {
    public static void main(String[] args) {
        //让当前线程进入休眠,睡眠5秒
        //当前线程是主线程!!!
        /*
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         */
        //5秒之后执行这里的代码
        //System.out.println("hello world!");
        for (int i =0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadTest07 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t=new MyThread3();
        t.setName("t");
        t.start();
        //调用sleep方法
        try {
            //问题:这行代码会让线程t进入休眠状态吗?
            // 不会 sleep()方法是静态方法,实质上还是通过类名调用。
            t.sleep(1000*5);//在执行的时候还是会转换成:Thread.sleep(1000*5);
                                  //这行代码的作用是:让当前线程进入休眠,也就是main线程进入休眠
            System.out.println("hello world!!!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread3 extends Thread{
    public void run(){
        for (int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

终止线程的睡眠
注:这个不是中断线程的执行,是终止线程的睡眠

public class ThreadTest08 {
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        //希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了)
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //终止t线程的睡眠(这种中断线程睡眠的方式依靠了java的异常处理机制)
        t.interrupt();


    }
}
class MyRunnable2 implements Runnable{

    //重点:run() 当中的异常不能throws ,只能try catch
    //因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->begin");
        try {
            Thread.sleep(1000*60*60);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--->end");
    }

}

在java中怎么强行终止一个线程的执行。

以下这种方式存在很大的缺点: 容易丢失数据。因为这种方式是直接存线程杀死了。
线程没有保存的数据得会丢失。不建议使用。
public class ThreadTest09 {
    public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable3());
            t.setName("t");
            t.start();
            //模拟5秒
            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //5秒之后强行终止t线程
            t.stop();//已过时,不建议使用
    }
}

class MyRunnable3 implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

合理的终止线程的执行:

public class ThreadTest10 {
    public static void main(String[] args) {
        MyRunnable4 r=new MyRunnable4();
        Thread t=new Thread(r);
        t.setName("t");
        t.start();
        //模拟5秒
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //终止线程
        //想要什么时候终止t的执行,把标记修改为false就结束了。

        r.run=false;
    }
}
class MyRunnable4 implements Runnable{
    //打一个布尔标记
    boolean run=true;
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            if (run){
                System.out.println(Thread.currentThread().getName()+"--->"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                // return就结束了,在结束之前还有什么没保存的。
                //在这里可以保存呀。
                //save....

                //终止当前线程
                return;
            }

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值