线程的基本使用及概念介绍

线程的基本使用及概念介绍

进程:进程指正在运行的程序,确切的来讲,当一个程序进入内存运行,即变成一个进程,进程是出于运行过程中的程序,并具有一定的独立功能!
在这里插入图片描述
线程:是进程的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,事实上,我们现在使用的桌面软件都是多线程程序!就像你打开网易云音乐,既可以听歌,又可以看别人的评论,还能私信别人,这都是一个个的线程完成的不同的任务!

程序运行原理

分时调度

所有的线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间

抢占式调度

优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个,Java使用的是抢占式调度,cpu在使用抢占式调度模式在多个线程之间进行高速的切换,对于cpu的一个核而言,某个确定的时刻,只能运行一个程序,而cpu的多个线程间的切换速度相对我们的感觉来讲是十分快的,看上去就是在同一时刻运行呢。(对于我八核十六线程的机器来讲,就是在不同的核上运行的!)
多线程并不能提高程序的运行速度,但能够提高程序的运行效率,让cpu的使用率更高!

主线程

main方法
jvm启动后,必然有个执行路径从main方法开始,一直执行到main方法结束,这个线程在java中称之为主线程。当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后执行,我们可以使用Java的多线程来解决这个问题。

通过Thread类创建线程

构造方法

Thread(string name) {
    //: 分配新的Thread对象,将指定的name作为线程名称
}

新建一个类,继承Thread
在这里插入图片描述
这个线程的主要作用就是执行for循环100次,并输出当前线程名和循环计数器i
在随便新建一个类,写一个main方法(main本身也是一个线程)

package com.wudiqiang.Test;

/**
 * @Author: 着凉的皮皮虾
 * @Date: 2019/7/27 20:09
 */
public class ThreadDemo {
    public static void main(String[] args) {
        long l1 = System.currentTimeMillis();

        for (int i = 0; i < 100; i++) {
            TestThread testThread = new TestThread("xxxxxxxxxxxxxxxxxx" + i * i);
            testThread.start();
            System.out.println(Thread.currentThread().getName() + i);
        }
        long l2 = System.currentTimeMillis();
        System.out.println("八核十六线程的机器执行100个线程100次for循环的时间为:"+ (l2 - l1) + "毫秒!");
    }
}

当我们运行此main方法的时候,每一次循环都会开一个线程最后会有100个线程同时执行100次for循环,最后来看一下输出的结果
在这里插入图片描述
为啥子这段话没输出到最后,是因为循环里开启了其他的线程,并没有运行结束,而main方法已经运行结束了
**注:线程对象调用run方法不开启线程,仅仅是对象调用方法,线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行!!

创建线程的目的**

为了建立程序单独执行的路径,让多部份代码同时执行,也就是说线程创建并执行需要给定线程要执行的任务(通过反射调用不同的类来执行多线程中不同的任务,线程是从线程池拿的,这是我前段时间做的一个项目)
主线程的任务定义在main函数中,自定以线程需要执行的任务都定义在run方法中。
Thread类run方法中的任务并不是我们所需要的,只有重写这个run方法。既然Thread类已经定义了线程任务的编写位置(run)方法,那么只要在编写位置中定义任务代码即可,所以进行了重写run方法动作(一般的多线程程序都是实现runnable接口,在面向对象语言中,接口就是赋予类以能力,我们想要啥能力,就实现啥接口,况且java是多实现,单继承的结构,如此一来,用哪个自然就很清晰了,当然也不排除某一部分的多线程程序,就是单独的执行这个任务,在没有其他的功能,这种情况下继承Thread还是实现runnable就看个人选择了)

多线程的内存

Java虚拟机的多线程是通过线程的轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令,因此,为了线程切换后能恢复到正确的位置,每条线程都需一个独立的程序计数器。
如果对虚拟机有一定的了解,应该清楚几个线程私有的内存模块:程序计数器以及虚拟机栈(大家所说的栈内存),既然这两个内存的模块是线程私有的,那么我们新开一个线程的时候,必然会为这个新线程划分出一块内存空间,来进行程序计数器以及虚拟机栈的各种操作,在虚拟机栈中,进行方法的压栈和弹栈,当线程的任务结束之后,它的专属的程序计数器和虚拟机栈也就莫得了,并不是线程在栈内存中释放。
如果线程执行的是一个Java方法,那么程序计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,这个计数器值则为空。

实现Runnable接口创建线程

实现Runnable接口,该类实现run方法,然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
它的意思就是呢:我创建一个类,这个类实现了runnable接口,然后实现了run方法,创建这个类的对象之后,将它扔到线程的构造方法中,就能开启线程了。
两种方法,一种继承Thread,一种实现接口创建对象,想用多线程就扔给Thread的构造函数,不想用就不扔!
来定义一个实现了runnable接口的类

package com.wudiqiang.Test;

/**
 * @Author: 着凉的皮皮虾
 * @Date: 2019/7/30 23:42
 */
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
    }
}

这个类就输出一个线程名字和当前的时间

package com.wudiqiang.Test;

/**
 * @Author: 着凉的皮皮虾
 * @Date: 2019/7/27 20:09
 */
public class ThreadDemo {
    public static void main(String[] args) {
        Runnable runnable = new TestRunnable();
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread.start();
        thread2.start();
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
    }
}

运行的结果应该是三个线程名+时间
在这里插入图片描述
由于超级棒的机器,三个线程是在同一时刻运行结束的(八核十六线程,如果是单核单线程的机器出现这种情况,就不是同一时刻运行的,而是cpu做了极快的切换来运行了三个所谓线程!)

实现Runnable的好处

避免了单继承的局限性,更加符合面向对象的思想,线程分为两部分,一部分线程对象(实现runnable的子类对象),一部分线程任务(Thread类对象)。继承Thread类,线程对象和线程任务耦合在一起,一旦创建Thread类的子类对象,又是线程对象,又是线程任务的。

线程的匿名内部类的使用

继承Thread类

package com.wudiqiang.Test;

/**
 * @Author: 着凉的皮皮虾
 * @Date: 2019/7/27 20:09
 */
public class ThreadDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "||||||||||||" +System.currentTimeMillis());
                }
            }.start();
        }
    }
}

在这里插入图片描述

实现Runnable接口

package com.wudiqiang.Test;

/**
 * @Author: 着凉的皮皮虾
 * @Date: 2019/7/27 20:09
 */
public class ThreadDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "||||||||||" + System.currentTimeMillis());
                }
            };
            new Thread(runnable).start();
        }
    }
}

时刻谨记,创建runnable的对象,一定要把对象扔进线程对象里才会开启新线程!
越是面向对象,步骤就越多,这样一来的确是降低了耦合,就像看spring和jdk的源码一样,翻来覆去的各种调用,慢慢的也就体会到了代码的艺术了。
线程对于一个合格的开发人员来讲是十分重要的知识,但是,线程是不可控的,Java中的线程是竞态的,所以,如何控制线程便成为一个十分重要的问题,各种各样的锁或者各种各样的处理策略,这都是一些人为的干涉的方法,并不能保证百分之百的控制性,在这种随机的情况下,线程会发生什么,我们很多时候是很难得知的,只能对有状态的类进行控制,保证共享数据的安全性以及操作的正确性。但是谁先上谁殿后,这就随缘了(谁先谁后不重要了其实,我们只要效率高就行,毕竟,男人,一定要快,男人写的程序,更得快)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值