java基础之多线程(概念)

1.进程与线程

进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。进程就是包括上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文,线程是共享了进程的上下文环境的更为细小的CPU时间。
——[知乎]

1.进程的基本定义类似于程序,是处于执行期间的程序及其所包含的资源的总称,而线程是进程中活动的对象。
2.进程是资源调度与分配的基本单位,线程是CPU调度的独立单位。
3.进程有独立的内存单元,一般来说一个进程崩溃对其他进程不会产生影响。线程自己有独立的栈,局部变量,寄存器,程序计数器等,但线程之间没有独立的地址空间,一个线程死掉会影响到进程,如果这个进程是单线程的就等于整个进程死掉。
4.一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

2.java程序启动至少会启动几个线程

除了main主线程以外,还有四个守护线程:
Finalizer 在垃圾回收之前调用对象的finalize方法的线程
Signal Dispatcher 为JVM处理本地操作系统信号的线程
Reference Handler 处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题
Attach Listener 接收到外部的命令并会交给signal dispather线程去进行

3. 多线程实现的方式

3.1. 继承Thread类,覆写其run方法

public class MyThread extends Thread {
    public void run(){
    System.out.println("MyThread running");
    }
}

3.2. 实现Runnable接口,实现run方法,Thread类也实现了Runable接口

public class MyThread implements Runnable {
    public void run(){
    System.out.println("MyThread running");
    }
}

3.3. 实现Callable接口,实现其call方法(JDK1.5)

public class MyThread implements implements Callable<String>{
    public String call() throws Exception {
    System.out.println("MyThread running");
    }
}

需要注意的是,执行 Callable 方式需要 FutureTask 实现类的支持,用于接收运算结果。

public void test(){
    MyThread td = new MyThread();
    FutureTask<String> result = new FutureTask<>(td);
    new Thread(result).start();
}

注:run()与start() 
run()方法并非是由刚创建的新线程所执行的,而是被创建新线程的当前线程所执行了。想要让创建的新线程执行run()方法就必须调用新线程的start()方法,但是此时线程只是处于就绪(可运行)状态,并没有立刻运行,等得到cpu时间片就开始执行run()方法。

4. 线程的状态

avatar
1、之前已经讲了实现方法,当我们new了这个对象后,线程就进入了初始状态;
2、当该对象调用了start()方法,就进入可运行状态;
3、进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态;
4、进入运行状态后情况就比较复杂了
4.1、run()方法或main()方法结束后,线程就进入终止状态;
4.2、当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片;
4.3、线程调用了yield()方法,意思是放弃当前获得的CPU时间片,回到可运行状态,这时与其他进程处于同等竞争状态,OS有可能会接着又让这个进程进入运行状态;
4.4、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配CPU时间片;
4.5、当线程调用wait()方法后会进入等待队列,线程被唤醒后会进入锁池,等待获取锁标记。
注: 
sleep():线程暂停执行,在执行的休眠时间内,该线程是不会请求CPU进行执行,但是并不释放所占有的资源。sleep()会抛出InterruptedException异常,其他线程可以调用这个线程的interrupt()方法向其发送中断信号。本线程可以用interrupted()来判断是否接收到中断信号。
join():在A线程中调用了另外一个线程对象B的join()方法时,那么A线程必须等待B线程执行完才能继续往下执行。
wait():进入这个状态会释放所占有的所有资源,与阻塞状态不同,进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒。由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程。

5. 守护线程

守护线程是为其他线程的运行提供服务的,如果所有的用户进程都运行结束了,那么守护进程也没有什么东西需要守护了,所以虚拟机也就退出了。我们可以自行设定守护线程。

public final void setDaemon(boolean on);

:thread.setDaemon(true)必须在thread.start()之前设置
在Daemon线程中产生的新线程也是Daemon的。

5.java.lang.ThreadGroup

ThreadGroup表示一组线程的集合,一旦一个线程归属到一个线程组之中后,就不能再更换其所在的线程组。

将一个线程放入线程组:

ThreadGroup threadGroup = new ThreadGroup("group");
Thread thread1 = new Thread(threadGroup,new Runnable() {
    public void run() {
        //运行···
    }
}); 

ThreadGroup有很多优势,最重要的一点就是它可以对线程进行遍历,知道那些线程已经运行完毕,还有那些线程在运行。

猜你喜欢

转载自blog.csdn.net/roubaoyin/article/details/79663394