关于JAVA开发中多线程相关问题

一、概念

        1.线程与进程

                进程:在内存中执行的应用程序(一个进程中至少有一个线程,还可以有多个线程)

                线程:是进程中的最小执行单元(一个功能需要一条线程执行)

                在CPU和内存之间为每一个功能开辟对应的通道,方便CPU去内存中提取代码做计算,这个通道就是线程。

                一个线程干一件事,同时干多件事就可以提高CPU的利用率。

        2.并发和并行

                并行:同一时刻,有多个事情在多个CPU上同时执行。

                并发:同一时刻,有多个事情在单个CPU上交替执行。

        3.CPU调度

                分时调度:指的是让所有线程轮流获取CPU的使用权,并且平均分配每个线程占用CPU 的时间。

                抢占式调度:多个线程轮流抢占CPU使用权,哪个线程先抢到,哪个线程先执行(java程序就是抢占式调度)。

二、创建线程的方式

        1.通过继承来创建线程 (extends Thread)

                1.首先创建一个MyThread类去继承Thread方法,然后重写Thread中的run方法

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

                2.创建一个测试类Test01

public class Test01 {
    public static void main(String[] args) {
        // 创建线程对象
        MyThread t1 = new MyThread();
        // 调用start方法, 启动线程
        t1.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"线程执行了:"+i);
        }
    }
}

    我们会发现一个现象,我们在控制台中,每次运行的结果都不一样。这就是发生了两个线程互相抢占CPU的使用权导致的。

        2.通过接口来创建线程(实现Runnable接口)

        首先,我们创建一个MyRunnable类去实现Runnable接口,并重写run方法,设置线程任务。

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
        }
    }
}

        创建测试类TestRunnable 

public class TestRunnable {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        /**
         * Thread(Runnable target)
         */
        Thread t1 = new Thread(myRunnable);
        //调用Thread中的start方法,开启线程
        t1.start();

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
        }
    }
}

关于这两种实现多线程的方式的区别:

1.继承Thread:继承只支持单继承,有继承的局限性
2.实现Runnable:没有继承的局限性, MyThread extends (父类) implements Runnable 

        3.使用匿名内部类创建多线程 

        创建测试类noNameTest

        

public class NoNa么Thread{
    public static void main(String[] args) {
        /*
          Thread(Runnable r)
          Thread(Runnable target, String name) :name指的是给线程设置名字
         */

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
                }
            }
        },"线程1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
                }
            }
        },"线程2").start();
    }
}

1.new Thread对应了Thread t1 = new Thread(myRunnable)红色字体

2.(new Runnable(){

        public void run(){

                (....................方法体)

        }

})对应了蓝色字体

        4.多线程在内存中的运行原理

        我们的程序从main方法开始运行,main方法在栈内存中开辟了空间,逐行向下执行。

        首先在堆内存中创建了MyThread对象(包括成员变量,方法表等),在栈内存中有MyThread类型的t1指向堆中MyThread的地址。

(关于内存分析,详见:关于面向对象的内存分析-CSDN博客

        然后t1.start()创建线程对象,会创建一个新的栈空间(属于t1线程),在t1中找run方法并运行。

        所以:开启一个线程就会开启一个新的栈空间去运行对应的线程代码。同一个线程对象不能连续调用多次start,如果想再次调用start,那就需要new一个新对象。

        5.Thread类中的主要方法

void start() -> 开启线程,jvm自动调用run方法
void run()  -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法
String getName()  -> 获取线程名字
void setName(String name) -> 给线程设置名字
static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象)   
static void sleep(long millis)->线程睡眠,超时后自动醒来继续执行,传递的是毫秒值  

关于为什么在重写的run方法中有异常只能try,不能throws

原因:继承的Thread中的run方法没有抛异常,所以在子类中重写完run方法之后就不能抛,只能try...catch

        6.Thread类中的其他方法

void setPriority(int newPriority)   
-> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不稳定

int getPriority()  -> 获取线程优先级
  
void setDaemon(boolean on)  
-> 设置为守护线程,当非守护线程执行完毕,
守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,
在告知的过程中,守护线程会执行,只不过执行到半路就结束了
  
static void yield() -> 礼让线程,让当前线程让出CPU使用权
   
void join() -> 插入线程或者叫做插队线程  
        6.1.1  线程优先级

        依然沿用MyThread类

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

        创建测试类Test02

public class Test02 {
    public static void main(String[] args) {
        //创建两个线程对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        //更改两个线程对象的名字以便识别
        t1.setName("线程1");
        t2.setName("线程2");

        //设置两个线程优先级
        t1.setPriority(1);
        t2.setPriority(10);

        //启动两个线程
        t1.start();
        t2.start();

    }
}

线程优先级默认为5,最大值为10,最小值为1

        6.1.2  守护线程

        依然沿用MyThread类

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

        创建GuardianThread类作为守护线程

public class GuardianThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("守护线程运行了"+i+"次");
        }
    }
}

        创建测试类Test03

public class Test03 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        GuardianThread guardianThread = new GuardianThread();

        //将GuardianThread设置为守护线程
        guardianThread.setDaemon(true);

        //启动两个线程
        myThread.start();
        guardianThread.start();
    }
}

运行结果如下:

明显,当非守护线程结束时,守护线程也会结束,只不过告知需要时间,结束会有延迟。

        6.1.3  礼让线程

        场景说明:如果两个线程一起执行,可能会执行一会儿线程A,再执行一会线程B,或者可能线程A执行完毕了,线程B再执行.
        那么如果想让尽量让两个线程交替执行,就需要用到礼让线程
        注意:只是尽可能的平衡,不是绝对的你来我往,有可能线程A线程执行,然后礼让了,但是回头A又抢到CPU使用权了。

        创建yieldThread类

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            //调用yield方法
            Thread.yield();
        }
    }
}

        创建测试类Test04

public class Test04 {
    public static void main(String[] args) {
        yieldThread y1 = new yieldThread();
        yieldThread y2 = new yieldThread();

        y1.setName("y1线程");
        y2.setName("y2线程");

        y1.start();
        y2.start();
    }
}
        6.1.4  插入线程

        继续沿用MyThread类

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

        创建测试类Test05

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread1 t1 = new MyThread1();
        t1.setName("线程1");
        t1.start();

        /*
          表示把t1插入到当前线程之前,t1要插到main线程之前,所以当前线程就是main线程
         */
        t1.join();

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"执行了......"+i);
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值