Java 线程总结

来源:互联网 发布:政府软件外包项目 编辑:程序博客网 时间:2024/06/03 21:00

1.进程:是一个正在执行的中的程序。每一个进程都有一个执行顺序,该顺序是一个执行路径。或者叫一个控制单元。

2.线程:就是进程中一个独立的控制单元。线程控制着进程的的执行。

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

4.Java vm启动时会有一个进程java.exe。 该进程中至少有一个线程负责java程序的执行。而且,这个线程运行的代码存在在main方法中,该线程称为主线程。

5.扩展:其实更细节说明jvm,jvm启动不止一个线程。还有负责垃圾回收机制的线程。

6.多个线程同时执行,会发现每次执行的结果都不一样。因为多个线程都获取cpu的执行权。CPU执行到谁,谁就执行。明确一点,在某一时刻,只能有一个线程在运行(多核除外。)。CPU在做着快速的切换,以达到看上去同时执行的效果。

7.多线程的一个特性:随即性,谁抢到谁执行,至于执行多长,cpu说的算。

8.获取名字:getName。线程有默认的名字:Thread-编号(从0开始)。

9.设置名称:setName或者构造方法。new Thread(“name”)。子类重写构造方法,调用父类super(“name”)。

10. 多个线程公用一个对象:new Thread(Runnable  target)。

11.死锁:同步中嵌套同步。

12.线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。

13.Wait(),notify(),notifyAll()都使用在同步中,因为要持有监视器(锁)的线程。

14.如果某个 synchronized  方法是 static     的,那么当线程访问该方法时,它锁的并不是synchronized 方法所在的对象,而是synchronized 方法所在的对象所对应的Class 对 象,因为Java  中无论一个类有多少个对象,这些对象会对应唯一一个Class 对象,因此当线程分别访问同一个类的两个对象的两个 static,synchronized 方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。 

15. synchronized 块,写法: 

  synchronized(object) 

   { 

   } 

 表示线程在执行的时候会对object 对象上锁。 

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

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

18.代码中要明确哪个锁需要wait();

例如:Object.wait(),Object.notify();

19. 另一个会导致线程暂停的方法就是Thread 类的sleep 方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。 

20.线程池:等待线程都存在线程池当中。Notify按顺序唤醒池中的线程,一般唤醒第一个睡眠的线程。notifyAll唤醒全部线程。然后抢夺执行权。

创建线程的方式:

1.继承Thread类。

1)定义类继承Thread。

2)覆写Thread类中的run方法。目的:存储代码,让线程运行。

3)调用线程的start方法。该方法有两个作用:启动线程,调用run方法。Start为线程分配必须的系统资源、调度线程运行并执行run。

2.实现Runable接口步骤

1)定义类实现Runnable接口。

2)覆盖Runnable接口中的run方法。:存储代码。

3)通过Thread类建立线程对象。

4)将Runnable接口的子类对象作为实际参数传递Thread类的构造方法。原因:自定义的run方法所属对象是Runnable接口的子类对象。所以要让线程去指定指定对象的run方法,就必须明确该run方法所属的对象。

5)调用Thread类的static方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别:

1.实现方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。

2.线程代码存放的位置不同。

3.实现方式可用于多个线程执行一个对象。(售票例子)

多线程安全问题:

问题原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只能执行了一部分,还没执行完。另一个线程参与进来执行。导致共享数据的错误。

解决办法:对多条操作共享数据的语句。只能让一个线程都执行完。在执行过程中,其他线程不可以参与进行。

1.  同步代码块:

Synchronized(对象){

                     需要被同步的代码块;

                     }

对象可以为任意对象值,当两个Synchronized语句中对象相同时,执行任意一个代码块中的内容时,另一个也不能被执行。

对象如同锁,持有锁的线程可以再同步中执行。没有锁的线程即使获得cpu的执行权,也进不去,因为没有获取锁。

2.同步函数:Synchronized修饰方法。

同步的前提:

1.必须要有两个或两个以上的线程。

2.必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

 如何找线程安全问题:

1.明确哪些代码是多线程运行的代码。

2.明确共享数据。

3.明确多线程运行代码中哪些语句是操作共享数据的。

JDK1.5后提供了多线程升级解决方案:

将同步Synchronized替换成现实Lock操作 。将Object中的wait、notify、notifyAll,替换成了Condition对象中await,signal,signalAll。使用时,莫忘使用finally{lock.unlock}解锁。Lock是个接口,一般使用它的实现类ReentrantLock,再由他(newCondition()方法)来获取condition对象。比较高级的是他可以唤醒不同对的condition对象对应的锁。即本方只唤醒对方操作。这样就可以唤醒指定的锁了。

停止线程:

原因:遇到特殊情况,线程处于冻结状态。读不到标记,线程无法结束。

解决办法:只有一种,让run方法结束。不能使用Thread 类的stop 方法(已过时)来终止线程的执行。

开启多线程运行,运行代码通常是循环结构。只要控制住循环结构,就可以让run方法结束,也就是线程结束。

方法1:一般要设定一个变量,在 run 方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。

方法2:当没有指定的方式让冻结状态的线程恢复到运行状态时,此时方法1不适用。这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

Thread类提供的方法是:interrupt();线程对象调用该方法,中断状态被清除(不是中断线程),它还将收到一个 InterruptedException。可以主线程中某处添加该方法,同时在run方法中的try catch( )中添加结束的条件(例如将flag变为false)。

关于锁的问题:

 Java 中的每个对象都有一个锁或者叫做监视器(monitor),当访问某个对象的synchronized 方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized 方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常), 那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized 方法。

如果一个对象有多个 synchronized 方法,某一时刻某个线程已经进入到了某个 synchronized 方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized 方法的。 

1.为什么要覆盖run方法?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说,run方法的作用是存储线程要运行的代码。

2.同步函数用的是哪一个锁呢?

  函数需要被对象调用。那么函数都有一个所属对象,就是this。所以同步函数的锁就是this。

3.如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证发现不是this,因为静态方法中也不可以this。静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class  该对象的类型是class。

静态的同步方法,使用的锁是该方法所在类的字节码文件对象:

类名.class。

4.为什么这些操作线程的方法(wait,notify,notifyAll)要定义在Object类中?

因为这些方法在操作同步线程时,都必须要标识他们所操作线程锁,只有同一个锁上的等待线程,可以被同一个锁notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒的必须是同一锁。而锁可以使任意对象,所以可以被任意对象调用的方法定义Object类中。

5.对于多个生产者和消费者。

1)为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

2)为什么要定义notifyAll。

    原因:因为需要唤醒对方线程。而只有notify,容易出现只唤醒本方线程的情况。While循环判断导致程序中的所有线程都等待。(只唤醒一个线程,不能确定是哪方的。)

  

1.static Thread  currentThread():返回对当前正在执行的线程对象的引用。

2.String getName() :获取线程名称。

3.Void setName(String name):设置名称。

4.Static void sleep(long millis):睡眠。(会抛异常,要捕获)

5. final  void  setDaemon(boolean on):将该线程标记为守护线程或后台线程,用户线程。当正在运行的线程都是守护线程时,即前台线程全部结束时,java虚拟机退出 。该方法必须在启动线程前调用。(主线程是前台线程)。

6. Void  jion():当A线程执行到了B线程.jion()方法时,A就会等待。等B线程都执行完,A才会执行。Jion可以用来临时加入线程执行。

7.String  toString():返回该线程的字符串表示形式,包括线程名称,优先级,线程组。

8.Void  setPriority(int newPriority):设置优先级。MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY

9. Static  void  yield():暂停当前正在执行的线程对象,并执行对其他线程。

1. 创建状态 

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

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

2. 可运行状态 

•  执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体 —run()方法,这样就使得该线程处于可运行( Runnable )状态。这一状态并不是运行中状态(Running ) ,因为线程也许实际上并未真正运行。 

3.不可运行状态 

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

•  调用了sleep ()方法; 

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

•  线程输入/输出阻塞 

返回可运行状态: 

处于睡眠状态的线程在指定的时间过去后,或者线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件满足。还有就是线程是因为输入/输出阻塞,等待输入/输出完成 

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

1.  线程的优先级及其设置 

设置优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。 

一个线程的优先级设置遵从以下原则: 

     – 线程创建时,子继承父的优先级 

     – 线程创建后,可通过调用setPriority()方法改变优先级。 

     – 线程的优先级是1-10之间的正整数。 

       1 - MIN_PRIORITY, 

       10 – MAX_PRIORITY 

       5- NORM_PRIORITY 

2.线程的调度策略 

线程调度器选择优先级最高的线程运行。但是,如果发生以情况,就会终止线程的运行。 

• 线程体中调用了yield()方法,让出了对CPU的占用权 

• 线程体中调用了sleep()方法, 使线程进入睡眠状态 

• 线程由于I/O操作而受阻塞 

• 另一个更高优先级的线程出现。 

• 在支持时间片的系统中,该线程的时间片用完。 

Thread 的一些常用方法。 

• 测试 threads: 

        isAlive() 

• Thread priority: 

            t getPriority() 

            t setPriority() 

• threads 进入非执行状态 

       Thread. sleep() 

       Thread. yield() 

同步机制:

1.为什么要引入同步机制 

在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。

解决方法:在线程使用一个资源时为其加锁即可。访问资源的第一个线程为其加上锁以后,其他线程便不能再使用 那个资源,除非被解锁。 

2.怎样实现同步 

 对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized 

例如: 

     synchronized void f() { /* ... */ } 

     synchronized void g() { /* ... */ } 

如果想保护某些资源不被多个线程同时访问,可以强制通过synchronized方法访问那些资源。调用synchronized方法时,对象就会被锁定。

说明: 

•  当synchronized方法执行完或发生异常时,会自动释放锁。 

•  被synchronized保护的数据应该是私有(private)的。 

线程组: 

所有线程都隶属于一个线程组。那可以是一个默认线程组,亦可是一个创建线程时明确指定的组。 

说明: 

在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组。 

若创建多个线程而不指定一个组,它们就会与创建它的线程属于同一个组。