并发编程01-初识多线程、基础概念

什么是进程和线程?

        进程是程序运行资源分配的最小单位

                进程内含有多条线程

                一个进程内的多条线程共享该线程内的全部系统资源

                进程与进程之间互相独立

                进程一般分为系统进程、用户进程

         线程是CPU调度的最小单位,必须依赖进程而存在

                线程基本上不拥有系统资源,仅拥有运行时必不可少的一点资源(程序计数器,寄存器,栈)

CPU核心数与线程的关系

        多核处理器

                单芯片多处理器(Chip MultiProcesssors 简称:CMP)集成的多核处理器,将大规模的并行处理器中的SMP(对称处理器)集成到同一芯片内,每个处理器处理不同的线程,这种依赖多个CPU(处理器)同时并行的运行程序是实现超高速计算的一个重要方向,成为并行处理。

        多线程(Simultanoes Multithreading 简称SMT)

                让同一个处理器上的多个线程同步执行并共享处理器的执行资源

        ec3892e41e4f4c7cbe0599f9cb94a736.png

        CPU时间片轮转机制

                从任务管理器中看出,3768个线程在运行,但是CPU只有16个,为什么使用时感受不到受到CPU线程数的限制。

                操作系统提供了一种CPU时间片轮转机制,最古老,最简单,最公平且使用最广的算法,又称RR调度,每个进程被分配一个时间段,称作该进程的时间片,即允许该进程运行的时间。

 并行与并发

        并行

                相对CPU而言,多核CPU同时工作,是真正意义上的并行处理。

        并发

                相对于应用程序而言,多个程序达到同时运行的效果,实际是得益于时间片轮转机制,任务的执行在不停的切换,交替执行。

        高并发编程

                充分利用CPU资源

                加快响应用户时间

                使代码简单化,模块化,异步化

                会产生线程安全问题

                线程之间会存在死锁问题

                线程过多,会将系统资源耗尽导致宕机

JAVA里的多线程

        JAVA天生多线程

                运行以下代码

    public static void main(String[] args) {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos =
                threadMXBean.dumpAllThreads(false,false);
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() 
                    + "]" + threadInfo.getThreadName());
        }
    }

              得到结果:

[6] Monitor Ctrl-Break //监控 Ctrl-Break 中断信号的
[5] Attach Listener //内存 dump,线程 dump,类信息统计,获取系统属性等
[4] Signal Dispatcher // 分发处理发送给 JVM 信号的线程
[3] Finalizer // 调用对象 finalize 方法的线程
[2] Reference Handler//清除 Reference 的线程
[1] main //main 线程,用户程序入口

           一个JAVA程序从main()方法开始执行,按照逻辑看没有其他线程参与,实际上执行的就是一个多线程程序,执行main()方法的也是一个名为main的线程

        线程的启动与终止

                启动:

                实现一个线程有各种方式,比如继承Thread类,实现Runnable接口,实现Callable接口,使用线程池Executor Service。但是在JDK中源码如下:

There are two ways to create a new thread of execution. 
One is to declare a class to be a subclass of Thread. 
This subclass should override the run method of class Thread. 
An instance of the subclass can then be allocated and started. 
For example, a thread that computes primes larger 
than a stated value could be written as follows:
 
      class PrimeThread extends Thread {
          long minPrime;
          PrimeThread(long minPrime) {
              this.minPrime = minPrime;
          }
 
          public void run() {
              // compute primes larger than minPrime
               . . .
          }
      }
  
 
The following code would then create a thread and start it running:
      PrimeThread p = new PrimeThread(143);
      p.start();
  
The other way to create a thread is to 
declare a class that implements the Runnable interface.
That class then implements the run method. 
An instance of the class can then be allocated, 
passed as an argument when creating Thread, and started. 
The same example in this other style looks like the following:
 
      class PrimeRun implements Runnable {
          long minPrime;
          PrimeRun(long minPrime) {
              this.minPrime = minPrime;
          }
 
          public void run() {
              // compute primes larger than minPrime
               . . .
          }
      }

                明确指出,有2种方式去创建一个线程。

                这两种方式创建线程的启动方式如下:

public class Main {

    static class MyThread extends Thread{
        @Override
        public void run() {
            // TODO ...
            System.out.println("creat thread myThread");
        }
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            // TODO ...
            System.out.println("creat thread myRunnable");
        }
    }

    public static void main(String[] args) {
        // MyThread
        MyThread myThread = new MyThread();
        myThread.start();
        // MyRunnable
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
    }
}

                 运行结果如下:

creat thread myThread
creat thread myRunnable

                终止:

                1:run() 方法结束

                2:抛出一个未处理的异常

         Thread与Runnable的区别

                Thread才是JAVA里线程的唯一抽象,Runnable只是对业务逻辑的抽象,Thread可以接受任意一个实现Runnable的实例并执行,为什么设计继承父类与实现接口两种方式?为了满足不同业务场景下的需求,JAVA只能单继承,但是可以多实现。当继承了其他业务类,无法继承Thread类时,可以使用实现Runnable接口去创建线程

        常用的多线程方法

                启动线程:O.start()

                结束线程:O.suspend(),O.resume(),O.stop() 【分别对应:暂停,重启,停止。这三种方法不建议使用,调用后,线程不会释放已有资源,比如锁等,可能导致程序工作环境状态不确定继而引发一系列问题,比如死锁】                                                      

                                  O.interrupt() 【中断标志,不代表立即中断,也可能不会中断,线程会通过该标志位来响应】可以通过调用 O.isInterrupted() 来判断该线程是否被中断(标志)也可以调用静态方法Thread.interrupted() 来判断,但是该方法会将中断标志改为false。

如果一个线程阻塞状态,如sleep(),join(),wait()等,如果发现中断标志位为true,会立即抛出InterruptedException异常,并立即把该中断标志改为false,死锁状态的线程也无法被中断

        run()和start()

                run() 是业务逻辑的实现,重写的方法,可以重复调用。

                start() 是让一个线程改为就绪状态,进入线程队列等待分配cpu,分到cpu时就会调用run() 方法,一个线程只能调用一次,多次调用抛出异常。

        线程优先级

                priority:线程构建时,可以通过 setPriority(int) 方法来修改线程的优先级(1~10),默认为5,优先级高的线程分配的时间片数量要高于优先级低的线程。

                一般会将频繁阻塞的线程设置较高的优先级,比较吃资源或偏重运算的线程设置较低的优先级,确保合理规划cpu时间片的分配,避免cpu被独占。

        守护线程

                Daemon(守护)线程是一种支持型线程,可以调用 setDaemon(true) 将线程设为守护线程,如:GC。在一个虚拟机中一旦不存在非守护线程,虚拟机会立即退出,此时守护线程也会立即销毁(finally块可能不会被执行)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值