多线程基础知识总结

第一章:Java多线程特性

1.线程的两种创建方法
一种是继承Thread类,另一种是实现Runnable接口

2.执行start()方法顺序不代表线程启动的顺序。

3.通过在run()方法前加入synchronized关键字,使得多个线程在执行run方法时,以排队的方式进行处理。synchronized可以在任意对象及方法上加加锁,而加锁的这段代码称为“互斥区”或“临界区”。

4.非线性安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。所以为了防止发生非线程安全问题,还是继续使用同步方法。

5.方法isAlive()是判断当前线程是否处于活动状态,活动状态就是线程已经启动且尚未
终止。

6.方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”(是指this.currentThread()返回的线程)休眠(暂停执行)

7.main线程和子线程sleepThread是异步执行的,
(1)不加join,先执行主线程,在执行子线程,
(2)加join,先执行子线程,主线程在子线程执行完毕后执行

8.getid()方法的作用是取得线程的唯一标识。

9.java有以下3中方法可以终止正在运行的线程
(1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
(2)使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果,比如:有可能使一些清理性的工作得不到完成,另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
(3)使用interrupt方法中断线程。

10.判断线程是否是否停止状态
(1)this.interrupted():测试当前线程是否已经中断状态,执行后具有将状态标志置清除为false的功能。 
(2)this.isInterrupted():测试线程Thread对象是否已经是中断状态,但不清楚状态标志

源码:
public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

public boolean isInterrupted() {
        return isInterrupted(false);
    }
11.停止线程的方法-异常法
在子线程中达到中断条件时,向外抛出一个中断异常,在用try-catch进行捕获,达到中断线程的方法。

12.使用return停止线程
将方法interrupt()与return结合使用也能实现停止线程的效果不过还是建议使用“抛异常”的方法来实现线程的停止,因为在cation块中还可以将异常向上抛,使线程停止的事件得以传播

12.如果在sleep状态下停止某一个线程,会进入catch语句,并且清除停止状态值,使之变成false

13.在使用suspend与resume方法时,如果使用不当,极易造 成公共的同步对象的独占,使的其他线程无法访问公共同步对象

14.yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

15.线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

16.线程的优先级与代码执行顺序无关,线程的优先级具有一定的规则性,也就是CPU尽量将执行资源让给优先级比较高的线程,也就是说优先级较高的线程不一定每一次都
先执行完。

17.在JAVA线程中有两种线程,一种是用户线程,另一种是守护线程守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。

第二章 对象及变量的并发访问

1.非线程安全问题存在于实例变量中,如果是方法内部的私有变量,则不存在非线程安全问题,所得结果也就是线程安全的了。

2.关键字synchronized取得的锁都是对象锁,而不是把一段代码或函数当做锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象,但如果多个线程访问多个对象,则JVM会创建多个锁。

3.调用关键字synchronized声明的方法一定是排队运行的。

4.可重入锁:自己可以再次获取自己的内部锁,当存在父子类继承关系是,子类是完全可以通过“可重入锁”调用父类的同步方法的。

5.当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

6.同步不能继承,所以还得在子类的方法中添加synchronized关键字

7.在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将会阻塞,这说明synchronized使用的“对象监视器”是一个。

8.同步synchronized(this)代码块时锁定当前对象的。

9.synchronized(非this对象x)格式的写法是将x对象本身作为"对象监视器",这样就可得到
以下3个结论
(1)当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
(2)当其他线程执行x对象中synchronzed同步方法时呈同步效果。
(3)当其他线程执行x对象方法里面的synchronized关键字的方法时,还是异步调用。

10.关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的*.java文件对应的Class类进行持锁。

11.synchronized关键字加到static静态方法上是给Class类上锁,可以对类的所有对象实例起作用,而synchronized关键字加到非static方法上是给对象上锁。

12.同步synchronzied(class)代码块的作用其他和synchronized static 方法的作用一样

13.关键字volatile的主要作用是使变量在多个线程间可见,强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

14.关键字synchronized和volatile比较
(1)关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。

(2)多线程访问volatile不会发生阻塞,而synchronzed会出现阻塞。

(3)vlolatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存的数据做同步。

(4)关键字volatile解决的是变量在多个线程之间的可见性;而synchrnozed关键字解决的是多个线程之间访问资源的同步性;

线程安全保护原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程
安全的。

15.关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但注意的是:如果修改实例变量中的数据,比如i++,也就是i=i+1,则这样的操作其实并不是一个原子操作票,也就是非线程安全的。表达式i++的操作步骤分解如下:
(1)从内存中取出i的值
(2)计算i的值
(3)将i的值写到内存中
所以说volatile本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存的,对于多个线程访问同一个实例变量还是需要加锁同步。

16.除了在i++ 操作时使用synchronzed关键字实现同步外,还可以使用AtomicInteger原子类进行实现。

17.关键字synchronzed可以使多个线程访问同一个资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能;

关键字synchronzed可以保证在同一时刻,只有一个线程可以执行某一个方法或某一代码块;它包含两个特征:互斥性和可见性。同步synchronzed不仅可以解析一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都可以有同一个锁保护之前所有的修改效果。

第三章 线程间通信

1.wait使线程停止运行,而notify使停止的线程继续运行。

2.通过调用wait()方法可以使临界区的线程进入等待状态,同时释放同步对象的锁。notity操作可以唤醒一个因调用了wait操作而处于阻塞状态的线程,使其进入就绪状态。被重新唤醒的线程会试图重新获取临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码。wait()方法可以使调用该的方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

3.notity()方法可以随机唤醒等待队列中等待同一共享资源的“一个”线程,并使该线程退出
等待队列,进入到可运行状态,也就是notity()方法仅通知“一个”线程。
notityAll()方法可以使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入到可运行状态。此时,优先级最高的那个线程最先执行,但也有可能随机执行,因为这要取决于JVM虚拟机的实现。

4.出现阻塞的情况大体分为如下5种:
(1)线程调用slep方法,主动放弃占用的处理器资源
(2)线程调用了阻塞IO方法,在该方法返回前,该线程被阻塞。
(3)线程视图获得一个同步监视器,但该同步监视器正在被其他线程所持有
(4)线程等待某一通知
(5)程序调用了supend方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法。

5.每个锁对象都有两个队列,一个是就绪队列,一个阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度;反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。

6.当方法wait()方法执行后,锁被自动释放,但执行完notify()方法,锁却不自动释放,必须执行完notify()方法所在的同步synchronzed代码后才释放锁。

7.当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常。

8.在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。

9.调用方法notify()一次只随机通知一个线程进行唤醒

10.多生产和多消费出现"假死",其实就是线程进入等待状态。如果全部线程都进入等待状态,则程序就不再执行任何业务功能了,整个项目呈停止状态。

解析:wait/notify进行通信,但不保证nofity唤醒的是异类,也许是同类,比如“生产者”唤醒生产者,或者"消费者"唤醒"消费者"这样的情况,如果按这样情况运行的比率极少成多,就会导致所有的线程都不能续假运行下去,大家都在等待,都呈WAITIN状态,程序最后也就呈“假死”状态,不能续假运行下去了。

11.管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读数据。

12.join()的作用是等待线程对象销毁。是使所属的线程对象X正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程X销毁后在继续执行线程z后面的代码。

  方法join具有使线程排队运行的作用,有些类似同步的运行效果。join和synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。

13.在join过程中,如果当前线程对象被中断,则当前线程出现异常

14.方法join(long)中的参数是设定等待时间。

15.方法join(long)和sleep(long)区别方法join(long)的功能在内部时使用wait(long)方法来实现的,所以join(long)方法具有十分锁的特点,当实现wait(long)方法后,当前线程的锁被释放,那么其他线程就可以调用此线程的同步方法了,而Thread.sleep(long)方法却不释放锁。

16.类ThreadLocal的使用变量值的共享可以使用public static变量的形式,所有的线程都是用同一个public static变量。如果想实现每一个线程都有自己的共享变量,可以使用ThreadLocal解决每一线程绑定自己的值,变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值可以放入Threadlocal类中进行保存。

17.使用类InheritableThreadLocal可以在子线程中取得父线程继承下来的值。

第四章 Lock的使用

1.java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,新增了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定‘多路分支通知等功能,而且在使用上也比synchronized更加的灵活。

2.调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放时在次争抢效果和使用synchronized关键字一样,线程之间还是顺序执行的。

3.使用Condition实现等待/通知关键字synchronzed与wait()和notify()/nitifyAll()方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。
区别:
Contion类具有更好的灵活性,比如可以实现多路通知功能,可就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condtion中,从而可以有选择地进行线程通知,在调度线程上更加灵活。

synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

4.等待/通知
(1)Object类的wait()方法相当于Condition类中的await()方法。
(2)Object类的wait(long timeout)方法相当于Condition类的await(long time,TimeUnit unit)方法。
(3)Object类中的notify()方法相当于Condition类中的signal()方法。
(4)Object类的notifyAll()方法相当于Condition类的signalAll()方法。

5.Conditino对象可以唤醒部分指定的线程,有助于提升程序运行的效率,可有先对线程进行分组,让后再唤醒指定组中的线程。

6.公平锁与非公平锁
公平与非公平锁:锁Lock分为“公平锁”和“非公共锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方法可能造成某些线程一直达不到锁。

7.int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

8.int getQueueLength()的作用是返回正等待获取此锁定的线程估计数,比如有5个线程,一个线程首先执行await()方法,那么在调用getQueueLength()方法后返回值为4,说明有4个线程同时在等待lock的释放。

9.int getWaitQueueLength(Condition condition)的作用是返回等待与此锁定相关条件Condition的线程估计数,比如有5个线程,每个线程都执行了同一个condition对象的await()方法,那么调用getWaitQueueLength(Condition conditon)方法返回的值为5.

10.方法boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此锁定。
方法 boolean hasQueueThreads()的作用是查询是否有线程正在等待获取此锁定。
方法 boolean hasWaites(Condition conditon)的作用是查询是否有线程正在等待与此锁定有关的condtion条件。

11.方法 boolean isFair()的作用是判断是不是公平锁。
默认情况下,ReentrantLock类使用的非公平锁。

12.方法boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定。

13.方法boolean isLocked()的作用是查询此锁定是否有任意线程保持。

14.方法void lockInterrupttibly()的作用是:如果当前线程未被中断,则获取锁定,如果已经被
中断则出现异常

15.方法booelan tryLock()的作用是,仅在调用锁定未被另一个线程保持的情况下,才获取该锁定

16.方法boolean tryLock(long timeout,TimeUnit unit)的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。

17.使用Condtiion实现顺序执行使用Condtion对象可以对线程执行的业务进行排序规划


18.使用ReentrantReadWriteLock类
 类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务。读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁来提升该方法的代码运行速度。

 读写锁表示也有两个锁,一个是读操作相关的锁,也就是共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁和写锁互斥,写锁和写锁互斥。在没有现成Thread进行写人操作时,进行读取操作的多个Thread都可以获取读锁,而进行写人操作的Thread只有在获取写
锁后才能进行写人操作。即多个Thread可以同时进行读取 操作,但是同一时间只允许一个Thread进行写入操作。

总结:“读写”、“写读”、“写写”都是互斥的;而“读读”是异步的,非互斥的。

第五章 定时器Timer

1.TimerTask是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务有可能消耗的时间较长,则后面的任务运行的时间也会被延迟。

2.方法schedule(TimerTask task,Date firstTime,long period)
该方法的作用是在指定的日期之后,按指定的间隔周期地无限循环地执行某一个任务
(1)计划时间晚于当前时间:在未来执行
(2)计划时间早于当前时间:提前运行如果计划时间早于当前时间,则立即执行task任务

3.TimerTask类的cancel()方法的作用是将自身从任务队列中清除,其他任务不受影响

4.Timer类的cancel()方法
和TimerTask类中的cancecl()方法清除自身不同,Timer类中的cancel()方法的作用是将任务队列中全部任务清空。

注意:Timer类中的cancel()方法有时并不一定会停止执行计划任务,而是正常执行;
原因是:Timer类中的cancel()方法有时并没有争抢到queue锁,所以TimerTask类中的任务继续正常执行

5.方法schedule(TimerTask task,long delay)方法
该方法的作用是以执行schedule(TimerTask task,long delay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。

6.方法schedule(TimerTask task,long delay,long period)方法
当前的时间为参考时间,在此时间基础上延迟指定的毫秒数,再以某一间隔无限次数地执行某一任务

注:凡是使用方法中带有period参数的,都是无限循环执行TimerTask中的任务。

7.方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
方法schedule和方法scheduleAtFixedRate都会按顺序执行,所以不要考虑非线程安全的情况。
方法schedule和scheduleAtFixedRate主要的区别只在于不延迟的情况。使用schedule方法:如果执行任务的时间没有被延迟,那么下一次任务的执行时间参考的是上一次任务的“开始”时的时间来计算。

使用scheduleAtFixedRate方法:如果执行任务的时间没有被延迟时,那么下一次任务的执行时间参考的是上一次任务的“结束”时的时间来计算。延迟的情况则没有区别,也就是使用schedule或scheduleAtFixedRate方法都是如果执行任务的时间被延迟,那么下一次任务的执行时间参考的是下一次任务“结束”时的时间来计算。

第六章 单例模式和多线程

1.立即加载/饿汉模式
立即加载就是使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化

2.延迟加载/懒汉模式
延迟加载就是调用get()方法时实例才被创建,常见的实现方法就是在get()方法中进行new实例化。

3.针对某些重要的代码进行单独的同步
同步代码块可以针对某些重要的代码进行单独的同步,而其他的代码则不需要同步。这样在运行时,效率完全可以得到大幅提升。

4.使用双重检查锁功能,成功地解决了懒汉模式遇到多线程的问题。DCL也是大多数多线程结合单例模式使用的解决方案。

5.使用内部类的方法实现单例模式

6.使用static代码块实现单例模式

7.使用enum枚举数据类型实现单例模式

第七章 知识补充

1.线程对象在不同的运行时期有不同的状态,状态信息就存在与State枚举类中
(1)NEW:至今尚未启动的线程处于这种状态
(2)RUNNABLE:正在Java虚拟机中执行的线程处于这种状态
(3)BLOCKED:受阻塞并等待某一监视器锁的线程处于这种状态
(4)WAITING:无限期地等待另一个线程来执行某一特定操作的线程处于这种状态
(5)TIMED_WAITING:等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态
(6)TERMINATED:已退出的线程处于这种状态

2.(1)NEW状态是线程实例化后还从未执行start()方法时的状态;
(2)RUNNABLE状态是线程进入运行的状态,
(3)TERMINATED是线程被销毁时的状态;
(4)TIMED_WAITING代表线程执行了Thread.sleep()方法,呈等待状态,等待时间到达,继续向下运行;即执行sleep()方法后线程的状态枚举值就是TIMED
(5)BLOCKED状态出现在某一个线程在等待锁的时候
(6)状态WAITING是线程执行了Object.wait()方法后所处的状态

3.线程组的作用是,可以批量的管理线程或线程组对象,有效地对线程或线程组对象进行组织

4.SimpleDateFormat非线程安全
类SimpleDateFormat主要负责日期的装换与格式化,但在多线程的环境中,使用此类容易造成数据装换及处理的不准确,因为SimpleDateFormat类并不是线程安全的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值