1.进程与线程:dos系统有一个非常明显的特点,一旦有一种病毒之后,系统就会立刻死机,因为dos系统是单进程的。

            线程间共享资源,而每个进程都有自己独立的内存,且难以共享和切换。

            线程是在进程基础上的进一步划分。而且线程是在进程上并发执行的。

            如果有多个任务同时执行,则所有的系统资源是共享的,被所有的线程公用,但是程序的执行需要cpu,当我们只有一个CPU时,在一个时间点上就只会有一个程序运行。

2.java中多线程的实现的两种方法:

            继承thread类:

               thread类是java.lang包中的,而这个包是在程序运行时自动导入的,所以我们不需要手动的导入包。

                继承了thread后这个类就有了开启多线程的能力,我们必须去复写run方法,将我们希望每个线程中执行的代码写到里面。

                然后我们创建这个类的对象,用对象调用start方法就可以了,关于start方法:start方法中有相关的判断,如果我们把一个对象调用两次start方法,也就是重复开启线程,start方法中会抛出异常。上面已经说过线程涉及到要抢占CPU,则需要调用系统底层的函数来计算那个多个线程如何占用cpu,所以在start方法中还有一个native关键字。它的意思就是去调用操作系统的函数完成相关的功能。

         实现runnable接口:

               让一个类实现runnable接口,这个类复写run方法,与上面的方法相同。

                创建该类的对象,让后将对象给Thread实例化,new Thread(myRunnable).start();    注意我们只要创建一次myRunnable对象,可以把后面的语句看成是开启一个线程,写几次就是开启几个线程。具体代码如下:

   那么两种多线程的实现方法有什么区别呢:

   如果你的类已经继承了别的类,那就只好去实现runnable接口了,java的单继承属性。

3.常见的问题      

    线程的名字:可以自己命名,虚拟机会自动分配名字,主线程的名字是mian,非主线程的名字不确定;线程的名字是可以获取的;

    获取当前线程对象的方法是:Thread.currentThread();

    线程使用start方法来启动但是执行时候并不是按照启动的顺序来的;

    当目标run()方法结束时该线程完成;

4.java线程栈模型:存储了内存中线程调度的栈信息,当前调用的方法总是位于栈顶;每个线程私有的;       

5.线程的生命周期

        线程有    创建、可运行、运行中、阻塞、死亡5 中状态。

                新建:线程对象已经创建,但是没有调用start方法来开启多线程;

                可运行状态:使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所有资源。线程不是启动就一定会被运行的,在等待运行的这段时间久是可运行状态;

                运行状态:java运行系统通过调度选中一个线程,使其占有CPU并转为运行中状态,此时系统真正执行线程的run()方法。

                阻塞、等待、睡眠状态:一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态。

                死亡状态:当线程的run方法执行完成后就认为它死去;这个线程的对象也许还是活着的,但是他已经不是一个单独执行的线程了;线程一旦死亡就不能复生;如果在一个死去的线程的对象上调用start方法或抛出异常;

6.阻止线程的运行

    除了io阻塞外,考虑下面的三种情况:

        睡眠:Thread对象中有一个静态的sleep方法,它可以强制当前正在执行的线程休眠;原因可能是线程执行太快了或者需要强制进入下一轮,java中没有保证合理的轮换;将该方法写入到run方法中就可以实现线程的睡眠;线程睡眠是帮助所有线程获得允许机会的好方法;线程睡眠时间到期后,会回到可运行状态而不是运行状态,也就是它不是一定会在苏醒后就立马得到运行机会;

        等待、对象锁;

  1. 线程的优先级和线程让步yield()

    优先级:JVM基于优先级来调度线程;大多数情况下当前运行的线程的优先级将大于或等于线程池中任何线程的优先级的,但这不是绝对的;优先级为我们提供一个调度线程执行顺序的方法;编程的时候不能过多的依赖优先级,因为它没有保障只是作为一种提高程序效率的方式;优先级范围是1-10;设置方法:线程对象.setPriority(n);默认为5;

    Thread.yield()方法的作用是让当前执行的进程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会;实际使用中它也不是一定能做到轮换的作用,因为有可能这个线程回到可运行状态后立即又被线程调度再次选中了;

    Thread的非静态方法join();使用方法:a线程对象.join();将当前线程加入到a线程对象的后面,直到a线程结束后才会执行当前的线程,这个地方我也不知道别的线程会不会受到影响,按照逻辑来说对别的线程是没有影响的;这个方法的实现其实是等待,该方法还可以传入等待的时间,这时就是等待a线程结束或者到达了等待的时间后当前的线程会回到可运行状态;

8.小结会导致线程离开运行状态的情况:

    线程的run方法运行完成了;    线程调度决定将线程由运行状态转移到可运行状态;    调用sleep方法使线程睡眠;    在对象上调用wait方法使线程等待;    调用yield方法使线程让步;    调用join方法使线程等待另一个线程先完成;    线程不能在对象上获得锁定,它正试图运行该对象的方法代码;

9.线程的同步锁

    线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏;

    锁:java中每个对象都有一个内置锁,当程序运行到非静态synchronized同步的方法时,自动获得于正在执行代码类的当前实例有关的锁(为什么是有关的,因为你的这个对象可能又指向了另一个对象,从而调用了些个关联的对象的同步方法或是代码块时它的锁也会被当前进程获得),这时别的线程不能进入该对象上的同步方法或代码块了(注意是对象,而不是类),直到该锁被释放,释放锁指的是持有锁的线程退出了同步方法或代码块;持有锁的线程封锁了该对象的所有同步方法,而非同步方法是可以自由访问的;

    synchronized:可以修饰一个方法,也可以修饰一个代码块,在修饰代码块是必须指定是要与哪个对象进行同步;不可以直接修饰一个变量;

    静态方法同步:由于在调用静态方法时,对象实例不一定被创建,因此,就不能使用this来同步静态方法,而必须使用Class对象来同步静态方法。

    阻塞:调用同一个对象的非静态的同步方法时会发生阻塞,调用同一个类中的静态同步方法的线程将阻塞,静态和非静态的同步方法不会彼此阻塞,对于同步代码块来说一定要看清指定的对象是否是同一个,不同对象上持有锁的线程是不会相互阻塞的;

10.线程安全类:当一个类已经很好的同步以保护了它的数据时我们称这个类是线程安全的;

11.线程死锁:两个线程相互阻塞,产生死锁;下面为死锁的一个例子:

    

  1. publicclass DeadlockRisk {

  2. private static class Resource {
    
  3.     public int value;
    
  4. } 
    
  5. private Resource resourceA =new Resource(); 
    
  6. private Resource resourceB =new Resource(); 
    
  7. public int read() {
    
  8.     synchronized (resourceA) {
    
  9.         synchronized (resourceB) {
    
  10.             return resourceB.value + resourceA.value;
    
  11.         } 
    
  12.     } 
    
  13. } 
    
  14. public void write(int a,int b) { 
    
  15.     synchronized (resourceB) {
    
  16.         synchronized (resourceA) {
    
  17.             resourceA.value = a; 
    
  18.             resourceB.value = b; 
    
  19.         } 
    
  20.     } 
    
  21. } 
    
  22. }

    如果一个线程调用了read,另一个线程调用的write,那么它们可能会一个持有一个A的锁另一个持有B的锁,那个它们就产生了死锁,其实上面的代码中产生死锁的概率是很小的,但毕竟有可能产生;

 

  ;