Java----多线程知识点归纳(概念)

一、线程与进程的区别:
    多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响. ?线程本身的数据通常只有寄存器数据,以及一个

  程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和

  I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

二、了解一下java在多线程中的基础知识:

  1.Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程
    (主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。

  2. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)

  3. 线程的实现有两种方式:

      第一种方式是继承Thread类,然后重写run方法;
      第二种是实现Runnable接口,然后实现其run方法。

  4. 将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,
      start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个
    类继承了Thread类之后,该类就叫做一个线程类。

  5. 一个进程至少要包含一个线程。

  6. 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。

  7. 对于双核或双核以上的CPU来说,可以真正做到微观并行。

三、Thread源码研究:

  1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法;

  2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象

   所共享(因为它是static的成员变量)。

  3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法

     此时什么事情也不做。

  4)当使用第二种方式生成线程对象时,我们需要实现Runnable接口的run方法,然后使用 new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)

  来生成线程对象,这时的线程对象的run方法或调就会MyThread类的run方法,这样我们自己编写的run方法就执行了。

  说明:
    Public void run(){
      If(target!=null){
      Target.run();
    }}
  当使用继承Thread生成线程对象时,target为空,什么也不执行,

  当使用第二种方式生成时,执行target.run(),target为runnable的实例对象,

  即为执行重写后的方法

总结:两种生成线程对象的区别:

  1.两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。

  2.在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,

     就应该用第二种方法来构造,即实现Runnable接口。

四:线程的生命周期:


  1).创建状态:
    当用new操作符创建一个新的线程对象时,该线程处于创建状态。

    处于创建状态的线程只是一个空的线程对象,系统不为它分配资源

  2). 可运行状态:
    执行线程的start()方法将为线程分配必须的系统资源,安排其运行,

    并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。

    这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。

  3).不可运行状态:

    当发生下列事件时,处于运行状态的线程会转入到不可运行状态。

    调用了sleep()方法;

    线程调用wait方法等待特定条件的满足

    线程输入/输出阻塞

  4)返回可运行状态:

    处于睡眠状态的线程在指定的时间过去后

    如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变

    如果线程是因为输入/输出阻塞,等待输入/输出完成

  5). 消亡状态

    当线程的run方法执行结束后,该线程自然消亡。

注意:

  1.停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。

    一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,

    如果满足条件则继续执行,否则跳出循环,线程结束。

  2.不能依靠线程的优先级来决定线程的执行顺序。

五:多线程并发:

    多线程并发是线程同步中比较常见的现象,java多线程为了避免

  多线程并发解决多线程共享数据同步问题提供了synchronized关键字

 synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

 1.Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),

当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程

都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后

(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问

该synchronized方法。

2. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了

某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问

该对象的任何synchronized方法的。

3.如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是

synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象

因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当

线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行

顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。

4. synchronized块,写法:

synchronized(object)
{
}
表示线程在执行的时候会对object对象上锁。

5.synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;
synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之
外的代码是可以被多个线程同时访问到的。

六:wait与notify

1.wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。
这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法
或块当中。当线程执行了wait方法时,它会释放掉对象的锁。

2. 另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的
过程中是不会释放掉对象的锁的。
3.notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的
其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:

o 通过执行此对象的同步实例方法。
o 通过执行在此对象上进行同步的 synchronized 语句的正文。
o 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器。

关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,
他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。
如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。

七:死锁的问题:


定义:线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,
确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。
当线程访问对象时,线程会给对象加锁
Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。下面笔者分析死锁的两个过程“上锁”和“锁死” 。
(1) 上锁
许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,就需要同步机制。因此大多数应用程序要求线程互相通信来
同步它们的动作,在 Java 程序中最简单实现同步的方法就是上锁。在 Java 编程中,所有的对象都有锁。线程可以使用 synchronized
关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。
为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。给共享变量上锁就使得 Java 线程能够快速方便地通信
和同步。某个线程若给一个对象上了锁,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,
直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,
这些睡眠进程就会被唤醒并移到准备就绪队列中。
(2)锁死
如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源,
系统中没有饿死和死锁的线程。当多个并发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程
必需的锁,互相等待时被阻塞就有可能出现死锁。
在编写多线程代码时,笔者认为死锁是最难处理的问题之一。因为死锁可能在最意想不到的地方发生,所以查找和修正它既费时又费力。
例如,常见的例子如下面这段程序。print?
1 public int sumArrays(int[] a1, int[] a2){
2 int value = 0;
3 int size = a1.length;
4 if (size == a2.length) {
5 synchronized(a1) { //1
6 synchronized(a2) { //2
7 for (int i=0; i<size; i++)
8 value += a1[i] + a2[i];
9 }
10 }
11 } return value;
12 }

这段代码在求和操作中访问两个数组对象之前锁定了这两个数组对象。它形式简短,编写也适合所要执行的任务;但不幸的是,
它有一个潜在的问题。这个问题就是它埋下了死锁的种子。
ThreadLocal类(这个类本人没用过,占时不太懂)
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是
该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程
中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建
的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map
中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个
共享对象本身,还是有并发访问问题。

时间: 2024-11-05 14:49:35

Java----多线程知识点归纳(概念)的相关文章

Java 多线程知识点整理

1.如何停止一个正在运行的线程? 使用共享变量,用volatile变量修饰,线程循环去访问该变量.如果线程因为发生某些事件进行阻塞,可以使用Thread的interrupt方法.可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码. class MyThread extends Thread { volatile boolean stop = false; public void run() { while (!stop) { System.out.println(g

Java多线程(1)——基本概念

一.基本概念 进程(process):程序的运行实例.进程与程序之间的关系就好比播放中的视频与对应的视频文件.进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位. 线程(thread):进程中可独立执行的最小单位.一个进程可以包含多个线程,同一个进程中的所有线程共享该进程中的资源.Java平台中的一个线程就是一个对象. 任务(task):线程所要完成的计算. 串行(sequential):顺序逐一完成. 并发(concurrent):同一时间段交替完成. 并行(parallel):同

java多线程知识点

下面是我学习多线程记录的知识点,并没详细讲解每个知识点,只是将重要的知识点记录下来,有时间可以看看,如果有不对的地方,欢迎大家支出,谢谢! 1.多线程的状态和创建方式:     线程的状态:          1.新状态:线程对象已经创建,还没有在其上调用start()方法.          2.可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态.当start()方法调用时,线程首先进入可运行状态.在线程运行之后或者从        阻塞.等待或睡眠状态回来后,也

java多线程之 基本概念

一.线程的五种状态 1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread().2. 就绪状态(Runnable): 也被称为“可执行状态”.线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程.例如,thread.start().处于就绪状态的线程,随时可能被CPU调度执行.3. 运行状态(Running) : 线程获取CPU权限进行执行.需要注意的是,线程只能从就绪状态进入到运行状态

java多线程知识点概述

这里只起一个概述的作用,极其简单的列一下知识点,需要在脑海中过一下,如果哪些方面不熟悉的话,建议利用网络资源去学习. 1.线程.进程概念 概念 线程状态及其转换 2.死锁.预防.解决 3.jdk线程实现方式 继承Thread重写run方法.实现Runnable实现run方法.实现Callable重写call方法 call方法能抛出异常允许有返回值 join/jield/interrupt/stop/wait/sleep/setDaemon/suspend 4.线程同步.线程异步 同步使用场景:共

软帝学院:java多线程知识点分享

1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 进程在执行过程中拥有独立的内存单元,而多个线程共享内存: 2.jvm多线程的启动是多线程吗? java的虚拟机jvm启动的是单线程,就有发生内存泄露的可能,而我们使用java程序没出现这样的问题, 也就是jvm启动至少有两个线程,一个执行java程序,一个执行垃圾回收.所以是多线程. 2.多线程的

java多线程知识点收集-总

1.线程与进程的关系 进程(Process):当一个程序进入内存开始运行时,就产生一个进程.进程是一个独立单元,它的资源由系统分配和调度. 线程(Thread):线程是进程的执行单元,线程在进程中是独立的.并发的执行流.当进程被初始化后,主线程(main)就被创建. 1)线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程. 2)线程拥有自己的堆栈.程序计数器以及局部变脸,但不拥有系统资源,它与父进程的其他线程共享共享父进程的全部资源. 3)线程的调度和管理由进程本身负责完成

java多线程知识点总结

1.线程调度知识:线程类Thread的了解,几个thread的方法.thread.sleep(),thread.join().(调用join方法的那个线程会立刻执行). object.wait()方法,notifyAll()方法. 2.线程间通信知识(共享资源):synchronized,volatile,readWriteLock,生产者消费者存储模型.队列.消息队列. 队列也算共享资源的访问啊.加锁在哪里加?(在共享对象里加锁) 3.实际中用到的地方:1.多线程访问缓存(数据队列,有生产者消

java多线程知识点汇总(四)多线程知识点脉络图

1.多线程安全问题 1)synchronized关键字:如何加锁的问题,选择synchronized方法还是synchnized代码块. 选择哪个锁问题,this对象,还是class对象(针对static方法或者变量),或者object对象 推荐使用同步代码块,因为加锁灵活,可以自己选择使用哪个锁,尤其一个类里面有多个同步方法时. 读写问题,保证多线程对同一对象的读写正常,不至于读到脏数据.读写方法共用同一个锁. 共享资源,这又涉及到单例模式的线程安全问题,线程池等. 2)线程安全类,比如str

java多线程知识点汇总(二)---多线程实例解析

本实验主要考察多线程对单例模式的操作,和多线程对同一资源的读取,两个知识.实验涉及到三个类: 1)一个pojo类Student,包括set/get方法. 2)一个线程类,设置student的成员变量age和name的值为111和111 3)另一个线程类,设置student的成员变量age和name的值为222和2222 4)main类,for循环200次,分别创建200个线程1和线程2对同一资源访问.(共400个线程) 1.第一种情况:饿汉式单例模式保证多线程操控的是同一对象 //饿汉式单例模式