十三、Java基础---------多线程总结

   多线程概述

     理解多线程首先应明确线程,要了解线程就必须了解什么是进程

1、进程

     是一个正在执行的程序。

     每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

2、线程

     就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。

     一个进程中至少有一个线程。

3、多线程

     在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。像这种在一个进程中有多个线程执行的方式,就叫做多线程。

4、运行多线程的实质

     我们电脑上有很多的程序在同时进行,就好像cpu在同时处理这所以程序一样。但是,在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。 而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程被cpu执行,或者说抢到了cpu的执行权,哪个线程就执行。而cpu不会只执行一个,当执行一个一会后,又会去执行另一个,或者说另一个抢走了cpu的执行权。至于究竟是怎么样执行的,只能由cpu决定。

开启线程的两种方式

1 、继承Thread,步骤如下:

     ※ 继承Thread类,要覆盖其run方法,起中run方法封装的是多线程要执行的业务代码

     ※ 调用线程的start方法。:

     class demo extends Thread{

          public void run(){

         }

     }

     demo demo=new demo();//创建对象就创建了一个线程。

注意:调用的是start方法而不是run方法,如果调用是run()方法,那么只是将run()当做一般方法对待而不是多线程。

2、实现Runnable接口

     1、 定义类实现Runnable接口。

     2、覆盖接口中的run方法(用于封装线程要运行的代码)。

     3、 通过Thread类创建线程对象;

     4、 将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。

     为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。

     5、 调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。

示例代码如下

     class Test implement Runnable

     {

         public void run(){ //子类覆盖接口中的run方法

           }

     }

     class ThreadTest {

        public static void main(String[] args) {

            Test t = new Test();实现Runnable接口的对象

            Thread t1 = new Thread(t);将那个对象作为参数传递到Thread构造函数

            Thread t2 = new Thread(t);

            t1.start();//调用Thread类的start方法开启线程

            t2.start();

        }

     }

两种方式开启多线程的区别

继承Thread类创建对象:

     1.Thread子类无法再从其它类继承(java语言单继承)。

     2.编写简单,run()方法的当前对象就是线程对象,可直接操作。

使用Runnable接口创建线程:

     1.可以将CPU,代码和数据分开,形成清晰的模型。

     2.线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法。

     3.有利于保持程序的设计风格一致。

     继承Thread类线程代码存放于Thread子类run方法中;

     实现Runnable接口线程代码存在于接口的子类run方法中,而且这种方式避免了单继承的局限性,在实际应用中,几乎都采取第二种方式。

线程的安全

导致安全问题的出现的原因:

     1.多个线程访问出现延迟

     2.线程随机性

     线程安全问题在理想状态下,不容易出现,但是一旦出现对软件的影响是非常大的

     解决方法:同步(synchronized

格式:

     synchronized(对象){

     需要同步的代码;

     }

     同步可以解决安全问题的根本原因就是在那个对象上,该对象如同锁的功能。

线程间通信

     其实就是多个线程在操作同一个资源,但是操作的动作不同。

等待唤醒机制理解:

     1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

          a,这些方法存在与同步中。

          b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

          c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

     2)wait(),sleep()有什么区别?

          wait():释放cpu执行权,释放锁。

          leep():释放cpu执行权,不释放锁。

     3)为甚么要定义notifyAll?

     因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。

   

JDK1.5中提供了多线程升级解决方案。

     将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

停止线程

     1.定义循环结束标记(run方法结束)

     因为线程运行代码一般都是循环,只要控制了循环即可。

     2.使用interrupt(中断)方法。

     该方法是结束线程的冻结状态,使线程回到运行状态中来。

     注:stop方法已经过时不再使用

特殊情况:

     当线程处于了冻结状态,就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

Thread类提供该方法 interrupt();

线程类的其他方法

守护线程

     setDaemon():将该线程标记为守护线程或用户线程,守护线程就相当于后台线程,当前台线程结束时,后台线程跟着也结束

注意:该方法必须在启动线程前调用。

     该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。

join()

     join方法 等待该线程终止。

     当A线程执行到了B线程的join方法时,A就会等待,等B线程都执行完,A才会执行。join可以用来临时加入线程执行。 如果B线程wait了主线程就挂了,B线程还能继续运行。

setPriority()

更改线程的优先级。三个优先级分别为:

     MAX_PRIORITY(最高优先级,10)

     MIN_PRIORITY(最低优先级,1)

     NORM_PRIORITY(默认优先级,5)

     首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。在其他情况下,线程优先级被设定为指定的newPriority 和该线程的线程组的最大允许优先级相比较小的一个。

yeild()

     暂停当前正在执行的线程对象,并执行其他线程。(使线程交替执行)

Thread中的toString方法返回该线程的字符串表现形式,包括线程名称、优先级和线程组。

     A开启的该线程,该线程就属于A组。

wait和sleep区别:分析这两个方法:从执行权和锁上来分析:

     wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。

     sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。

     wait:线程会释放执行权,而且线程会释放锁。

     sleep:线程会释放执行权,但是不释放锁。

原文地址:https://www.cnblogs.com/yueyazhishang/p/4030424.html