java多线程基础

最近在学java多线程,先对多线程做一个简单概要,后期再慢慢深入。

下面将从以下几点展开:

  1. 什么是多线程?为何要引入多线程?线程和进程之间的区别
  2. 多线程中常用方法小结
  3. 创建多线程的方式,Thread、Runnable俩种方式之间的区别?通过一个火车票的例子给出
  4. 线程生命周期
  5. 守护线程

1.什么是多线程?为何要引入多线程?进程和线程之间的区别
早期计算机,由于没有操作系统,一台计算机只能执行一个程序,并且这个程序能够访问计算机中所有资源。后来引入操作系统,操作系统使得计算机能够同时运行多个程序,并且每个程序是在不同的进程中执行,操作系统为每个进程分配资源。例如计算机中可以同时运行qq,浏览器等程序(进程是动态的,没有运行的程序不能称为进程)。
但是一个程序是一个进程,如何在一个进程中同时执行多个任务?比如说我运行qq,同时进行着和他人聊天以及接收文件,发送文件,如果没有多线程,一次只能干一件事,下一项任务必须等待上一项任务执行完毕。
串行化的工作方式无法使得资源得到充分利用,同时执行效率很低。比如说,在qq中,我先向一个好友发送了一个很大的文件,此时如果没有多线程技术,那么此时,我就需要等待文件发送完毕才能和另一个好友聊天。(很低效的工作方式)。
进程是由操作系统控制,操作系统能够同时运行的进程数量有限(视内存等因素决定,每个进程都需要占据一定的内存空间),线程是属于进程的一部分,可以在进程中创建多个线程,线程间独立执行(如果没有定义同步机制),多个线程共享进程资源。(所以多线程使用不当,容易引发问题,后期将对此做详细说明)。
综上,进程和线程都是为了提高资源利用率,提高工作效率诞生的,进程是由操作系统控制,能同时运行的进程数量有限,进程间相互独立(可以通过一些通信机制交换数据,学习操作系统)。线程是属于进程一部分,共享进程资源。

2.多线程中常用方法小结

线程创建:

  • Thread()
  • Thread(String name) 指定线程名称
  • Thread(Runnable target)
  • Thread(Runnable target,String name)

线程的方法:

  • void start() : 启动线程
  • String getName():返回该线程的名称
  • void join():
  • void join(long millis):等待该线程终止的最长时间为millis毫秒
  • void join(long millis,int nanos)
  • static void sleep()
  • static void sleep(long millis):让当前线程在指定时间内休眠
  • static void sleep(long millis,int nanos)
  • static void yield():暂定当前正在执行线程,让出cpu资源

    获取Thread引用

  • static Thread currentThread():返回当前正在执行的线程对象引用

3.创建多线程的方式,Thread、Runnable俩种方式之间的区别?
Thread方法创建线程:

/**
*通过继承Thread类,并重写run方法
*/
public class Task extends Thread{
    ...
    public void run(){
        ...
    }
    ...
}

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

通过Runnable方法创建:

/**
* 实现Runnable接口,实现run方法
*/
public class Task implements Runnable{
    ...
    public void run(){
        ...
    }
    ...
}

Thread thread = new Thread(new Task());//创建Thread对象
thread.start();

举例:火车站有五张西安-北京的火车票,分三个窗口卖票。
1.通过Thread方式实现:

public class Main {
    public static void main(String[] args){
        //定义三个线程模拟三个窗口
        Thread t1 = new TicketsThread("窗口1");
        Thread t2 = new TicketsThread("窗口2");
        Thread t3 = new TicketsThread("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

/**
 * 定义卖票的任务
 */
class TicketsThread extends Thread{
    private static int count = 5;//只有五张票,注意这里定义为static类型
    private String name;
    public TicketsThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        while (count > 0) {
            count--;
            System.out.println(name + "卖了1张票,剩余票数为" + count);
        }
    }
}

窗口2卖了1张票,剩余票数为3
窗口3卖了1张票,剩余票数为2
窗口1卖了1张票,剩余票数为3
窗口3卖了1张票,剩余票数为0
窗口2卖了1张票,剩余票数为1
//每次执行结果会不一样,取决于线程抢占cpu的随机性,出现俩次剩余票数为3的情况是由于run操作的非原子性,一个线程刚刚执行完count--操作,另一个线程抢占了cpu,同样执行了count--操作

2.通过实现Runnable接口模拟

public class Main1 {
    public static void main(String[] args) {
        TicketsThread1 tickets = new TicketsThread1();
        Thread t1 = new Thread(tickets, "窗口1");
        Thread t2 = new Thread(tickets, "窗口2");
        Thread t3 = new Thread(tickets, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class TicketsThread1 implements Runnable{
    private int count = 5;
    @Override
    public void run() {
        while (count > 0){
            count--;
            System.out.println(Thread.currentThread().getName() + "卖了1张票,剩余票数为:"+count);
        }
    }
}

窗口1卖了1张票,剩余票数为:4
窗口3卖了1张票,剩余票数为:2
窗口3卖了1张票,剩余票数为:1
窗口3卖了1张票,剩余票数为:0
窗口2卖了1张票,剩余票数为:3

说明:在继承Thread中,将count定义为静态变量,如果不定义为静态变量,每个窗口都可以卖5张票,总共卖出去会是15张票,这是因为定义了三个线程,每个线程虽然共享进程内存资源,但是每个线程有自己的本地内存副本,线程操作自己的本地副本。(这些将会在后期内存可见性中详述)。
在实现Runnable方法中,只初始化了一个任务实例,所以每个线程只对这一个任务实例进行操作。
java中只支持单继承,Runnable方式可以改变单继承的缺陷。

4.线程生命周期
这里写图片描述

在java程序中,使用new创建一个线程实例,线程调用start()方法,进入就绪状态,(此时线程进入线程队列,等待获取cpu资源),一旦获得cpu资源,线程进入运行状态,执行run方法,执行完毕后,线程消亡。如果在执行过程中,遇到阻塞事件,比如说等待键盘输入,sleep(),则让出cpu资源,进入阻塞状态,当阻塞解除,进入就绪状态。

在API文档中给出线程有6种状态:
- NEW:至今尚未启动的线程
- RUNNABLE:正在执行的线程
- BLOCKED:阻塞
- WAITING:无限期地等待另一个线程来执行某一操作的线程的处于地中状态
- TIMED_WAITING:等待指定时间
- TERMINATED:已退出的线程

5.守护线程
线程分为用户线程和守护线程
用户线程是运行在前台,执行具体任务,我们可以看得见的
守护线程运行在后台,为用户线程提供服务。

守护线程特点:

  • 一旦用户线程退出程序,守护线程没有守护对象,也就退出程序。
  • 在守护线程中创建的线程也属于守护线程。
  • 并不是所有线程都可以设置为守护线程,比如文件操作(一旦用户线程退出jvm,守护线程不能执行完任务退出,必须马上退出)

举例:jvm中一些监测线程,比如监测内存使用情况,GC
数据库连接池一些监测线程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值