1、集成Thread类
2、实现Runnable接口
1、currentThread()
获取当前运行的线程。
该方法将返回代码段正在被哪个线程调用的信息。
2、isAlive()
判断当前的线程是否处于活动状态。
如果线程未start(),返回false;
如果线程start(),未运行结束,返回true;
如果线程已经运行结束,返回false;
3、sleep()
在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。
这个“正在执行的线程”是指 this.currentThread() 返回的线程。
4、getId()
获取线程的唯一标识。
1、interrupt()
该方法仅仅是在当前线程打了一个标记,标记为中断状态,并不是真的停止线程。
如果想要停止线程,需要自己在线程类run()方法中通过获取当前线程的状态,进行判断和处理。
在sleep()状态下interrupt线程,会抛出InterruptedException,并清除interrupt状态。
先把线程打上interrupt()标记,再让线程sleep(),同样会抛出InterruptedException,并清除interrupt状态。
总结:只要一个线程同时满足interrupt()状态和sleep()状态,就会抛出InterruptedException,并且会清除interrupt状态。
2、interrupted()
测试当前线程是否已经中断。
执行本方法后,将会自动清除掉当前线程的interrupt()状态,即将已经打过interrupt()标记的线程,变为没有interrupt()标记。
3、isInterrupted()
测试线程是否已经中断。不会改变interrupt()状态。
4、异常法
在run()方法中,通过判断this.interrupted(),抛出一个异常,以此打断线程的正常执行顺序。
同时,在run()方法中,try()catch()异常。
5、暴力停止——stop()
该方法已经被作废。该方法将会抛出ThreadDeath异常,此异常不需要显示地捕捉。
作废原因:
①有可能使一些请理性的工作得不到完成
②对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
6、return
与interrupt()配合使用,和异常法相似。
更加推荐使用异常法,因为过多的return将会污染程序,而使用异常流能更好、更方便地控制程序的运行流程。
1、suspend()
使一个线程进入暂停状态,可恢复运行。
suspend()状态的线程isAlive()为true。
2、resume()
让一个陷入暂停状态的线程恢复运行。
3、suspend()和resume()的缺点
①独占
在使用公共的同步对象,也就是加锁对象时,由于自身陷入了暂停状态,锁得不到释放,其他线程也就无法获取加锁对象了。
例:System.out.println();
②不同步
因为线程的暂停而导致数据不同步。
方法执行一半被暂停了,方法的目的是改变对象的两个属性,结果刚改变了一个属性就被暂停,导致了数据的不同步。
4、yield()
放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。
有可能刚刚放弃,马上又获得CPU时间片。
1、setPriority()
线程可以划分优先级,优先级较高的线程得到的CPU资源较多。
设置线程优先级有助于帮“线程规划期”确定下一次选择哪一个线程来优先执行。
在Java中,线程的优先级分为1~10这10个等级,如果小于1或者大于10,则JDK将会抛出异常throw new IllegalArgumentException()。
JDK中使用三个常量来预置定义优先级的值,代码如下:
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
2、继承特性
在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。
3、规则性
CPU尽量将执行资源让给优先级比较高的线程。
4、随机性
线程的优先级与代码执行顺序无关。
优先级较高的线程不一定先执行完。
Java线程中有两种线程,一种是用户线程,另一种就是守护(Daemon)线程。
1、什么是守护线程?
守护线程是一种特殊的线程,它的特性有陪伴的含义,当进程中不存在非守护线程了,则守护线程自动销毁。
典型的守护线程就是垃圾回收线程(GC),当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。
1、A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
2、A线程先持有object对象的Lock锁,B线程如果需要在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。
3、脏读:只对赋值操作加锁,未对取值操作加锁。
4、synchronized锁重入
①前提
②可重入与线程安全
可重入的函数一定是线程安全的,反之则不一定成立。
③synchronized可重入锁的实现
每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。
④可重入锁具有继承性
当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。
这里的继承性,不是指的方法,是指的锁。
5、出现异常,锁自动释放
6、同步不具有继承性
父类某个方法加同步锁,子类继承父类,子类无锁。
1、synchronized(this)代码块是锁定当前对象的
2、可以将任意对象作为对象监视器
3、静态同步synchronized方法与synchronized(class)代码块
①synchronized加到static静态方法上是给Class类上锁。
②synchronized加到非static静态方法上是给对象上锁。
4、不使用String作为锁对象
5、同步synchronized方法无限等待与解决
如果一个类有多个方法都持有当前对象当锁,若其中一个方法进入死循环,其他同步对象将陷入无限等待。
解决:都用new Object()当锁,方法之间异步。
6、死锁问题
①死锁是程序设计的Bug,在设计程序时就要避免双方互相持有对方的锁的情况。
②死锁问题与锁是否嵌套无关,只要互相等待对方释放锁就有可能出现死锁。
在jdk安装目录的bin目录下,执行jps查看当前运行的main方法所在类的进程id,
再输入jstack -l id查看执行结果。
7、内部类与静态内部类
synchronized绑定了相同对象就是同步的,绑定不同对象就是异步的。
针对同步方法,只要对象不变,即使对象的属性被改变,运行的结果还是同步。
关键字volatile的主要作用是使变量在多个线程间可见。
线程安全包含原子性和可见性两个方面。volatile解决的是可见性,但是在JDK1.7之前才有用。
1、wait()
释放锁,可以加时间自动唤醒。
2、notify()
随机唤醒一个以参数为锁的线程,但是在当前线程的方法执行完之前,不会释放锁。
3、notifyAll()
唤醒所有线程。
4、通过管道进行线程间的通信:字节流
在Java的JDK中提供了4个类来使线程间可以进行通信:
1)PipedInputStream 和 PipedOutputStream
2)PipedReader 和 PipedWriter
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。
方法join()的作用是等待线程对象销毁。
1、作用
方法join的作用,是使对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。
方法join具有使线程排队运行的作用,有些类似同步的运行效果。
2、join与synchronized的区别是:
join在内部使用wait()方法进行等待,而sychronized关键字使用的是“对象监视器”原理做为同步。
3、join()方法与interrupt()方法如果彼此遇到,则会出现异常。
4、join(long)具有释放锁的特点。
变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static 变量。如果想实现每一个线程都有自己的共享变量该如何解决呢?JDK中提供的类ThreadLocal正是为了解决这个问题。
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。
1、ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值可以放入ThreadLock类中进行保存。
2、get()
默认返回null,可以通过继承ThreadLocal重写initialValue()方法的方式,修改默认返回值。
使用类InheritableThreadLocal可以在子线程中取得父线程继承下来的值。
1、获取锁的方法
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁;
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c) tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断;
2、unlock()方法
释放锁,最好放在finally代码块中执行。
3、Condition对象
4、公平锁与非公平锁
公平锁:线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。
非公平锁:抢占机制,随机获得锁。
通过new ReentrantLock(true)可以获取公平锁,new ReentrantLock(false)可以获取非公平锁。
new ReentrantLock()是非公平锁。
5、方法的讲解
1、读读共享
2、写写互斥
3、读写互斥
4、写读互斥