java中实现多线程的两种方式:
1.继承Thread
2.实现Runnable接口
isAlive()方法,用于测试线程是否处于活动状态,活动状态指的是线程已经启动但是还没有终止的状态。
停止线程的方法:
1.使用退出标志,使线程正常退出,也就是run方法完成后线程终止。
2.使用stop方法强制终止线程,不推荐使用,stop/suspend/resume的方法都是过时的方法。
3.使用interrupt方法中断线程。
interrupt/interrupted/isInterrupted的区别
interrupt:中断线程
interrupted:是静态方法,用于判断当前线程是否被中断,并且会清除中断标志位
isInterrupted:实例方法,用于判断线程是否中断,不会清除标志位
一般线程是不可终止的,使用中断来编写可终止的线程:
//1
public MyThread extends Thread{
public void run(){
try{
for(;;){
if(this.interrupted){
throw new InterruptedException();
}
}
//这句话不会执行
System.out.println("xxxx");
}
catch(InterruptedException e){
}
}
}
//2
public MyThread extends Thread{
public void run(){
for(;;){
if(this.interrupted){
break;
}
}
//这句话会执行
System.out.println("xxxx");
}
}
线程处于sleep状态时,被interrupt,此时线程中可以捕获到中断异常,并且中断的标志位会被清除。
suspend用于暂停线程,resume用于重启线程,不推荐使用,因为可能发生死锁。
//方法
public class A{
public synchronized void doSth{
//doSthing
Thread.currentThread().suspend();
//doSthing
}
}
//线程A中先调用doSth方法
//线程B中调用doSth方法,这样线程B永远被阻塞了
yield方法是放弃当前cpu的时间,但是放弃的时间确定,有可能刚放弃,又获得cpu的执行时间。
线程优先级具有继承性。
守护线程和用户线程,守护线程用于服务其他线程,当所有用户线程运行结束后,守护线程会自动结束。java中的垃圾回收线程就是典型的守护线程。
synchronized取得的锁都是对象锁,synchronized拥有重入锁的功能。
当一个线程执行的代码出现异常时,其所持有的锁将会自动释放。
以下几种场景是使用的同一个锁对象:
Object obj = new Object();
1.synchronized(obj)
2.obj中的synchronized方法
3.obj中的synchronized(this)方法
static方法前面加上synchronized,此时持有的是类的Class对象的锁。
尽量不要使用String对象作为锁对象,因为String使用了常量池,有可能造成死锁。
String aStr = "A";
String bStr = "A";
aStr == bStr //true
String aStr1 = new String("A");
String bStr1 = new String("A");
aStr1 == bStr1 //false
public void method(String str){
synchronized(str){
}
}
//线程A中调用
method("AA")
//线程B中调用,线程B和线程A持有的是同一个锁,如果线程A长时间执行,那么线程B将会被阻塞
method("AA")
线程之间是否同步的核心是看线程是否持有相同的锁对象。
volatile关键字解决了变量在多个线程之间的可见性,synchronized关键字解决了多个线程之间访问资源的同步性(同时确保了可见性和原子性)。
synchronized不仅可以解决一个线程看到的对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都可以看到由同一个锁保护之前所有的修改效果。以下例子说明问题:
////1.运行方式1
public static class Service{
private boolean b = true;
public void runMethod(){
while(b){
}
System.out.println("停止。。。。");//这句话在线程1中不会被执行到
}
public void stopMethod(){
this.b = false;
}
}
//mian函数中运行
Service service = new Service();
Runnable r1 = new Runnable(){
@Override
public void run() {
service.runMethod();
}
};
Runnable r2 = new Runnable(){
@Override
public void run() {
service.stopMethod();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
Thread.sleep(1000);
t2.start();
//2.运行方式2
public static class Service{
private boolean b = true;
public void runMethod(){
String a = new String();
while(b){
synchronized(a){}
}
System.out.println("停止。。。。");//这句话在线程1中会被执行到
}
public void stopMethod(){
this.b = false;
}
}
//mian函数中运行
Service service = new Service();
Runnable r1 = new Runnable(){
@Override
public void run() {
service.runMethod();
}
};
Runnable r2 = new Runnable(){
@Override
public void run() {
service.stopMethod();
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
Thread.sleep(1000);
t2.start();
原子类,只能保证原子类中的API是原子操作。
等待/通知机制:
wait()方法,将当前线程置入"预执行队列"中,并且在wait所在代码行处停止执行,直到接到通知或者被中断为止。在调用wait时,线程必须获得该对象的锁,执行wait方法后,当前线程释放锁,在从wait方法返回前,线程与其他线程竞争重新获取锁。
notify()方法在调用前,也需要获取对象的锁,用于通知正在等待该对象锁释放的其他线程,该方法会随机通知一个等待的线程,执行notify方法后,并不会立马释放锁,等待该线程的代码执行完后才会释放锁。
线程的状态:
- Running:运行状态
- Runnable:可运行状态
- Blocked:阻塞状态
每个锁对象都有两个队列,一个是就绪队列,用于放置将要获取锁的线程;一个是阻塞队列,用于放置被阻塞的线程。
线程的sleep方法并不会释放锁。
线程通过管道流进行通信:补充详细信息???
字节流:PipedInputStream/PipedOutputStream
字符流:PipedReader/PipedWriter
join方法:
当前线程等待子线程的销毁,当线程执行完毕终结时,会自动调用notifyAll方法。
join(int millis),等待确定的时间。
join方法内部使用wait来实现等待,当从join方法唤醒时需要重新竞争获取锁对象。
如果在join的过程中,该线程被中断,则会进入中断异常分支。
join和sleep方法的区别是,join获取锁后会释放锁,sleep不会释放锁。
ThreadLocal用于保存线程私有的变量,用法:
//定义公共变量,实际使用中将T替换为具体的类
public static ThreadLocal<T> LOCAL = new ThreadLocal<T>();
//在线程1中
T t = LOCAL.get();
LOCAL.set(xxx);
//在线程2中
T t = LOCAL.get();
LOCAL.set(xxx);
//给每个线程调用get时赋默认值,可以继承ThreadLocal类,覆写initialValue方法
public class CustomThreadLocal extends ThreadLocal<T>{
protected T initialValue(){
return xxx;
}
}
InheritableThreadLocal子线程可以继承父线程的值。
Lock(ReentrantLock)类可以实现更加灵活的同步,功能类似于synchronized关键字。
通过Lock对象可以创建多个Condition对象,Condition对象的await()/signal()/singalAll()方法等价于Object中的wait()/notify()/notifyAll()方法。
公平锁/非公平锁,在创建锁对象时传入boolean变量,表示是否使用公平锁。如果是公平锁按照FIFO的方式来分配锁,否则随机分配锁。
Lock Api说明:
int getHoldCount():查询当前线程保持此锁的个数
int getQueueLength():返回正等待获取此锁的线程估计数
int getWaitQueueLength(Condition condition):返回等待condition条件的线程数
boolean hasQueuedThread(Thread thread):指定的线程是否在等待获取锁
boolean hasQueuedThreads():是否有线程正在等待获取锁
boolean hasWaiters(Condition condition):是否有线程在等待获取condition条件
boolean isFair():是否是公平锁
boolean isHeldByCurrentThread():当前线程是否持有锁
boolean isLocked():锁对象是否被线程获取
void lockInterruptibly()
boolean tryLock()
Condition Api说明:
void awaitUninterruptibly()
void awaitUntil(Date deadline)
ReentrantReadWriteLock,拥有一个读锁、一个写锁,读与读不互斥,读与写互斥,写与写互斥。
Timer类的主要作用是设置计划任务。
TimerTask类用于封装任务。
在实例化Timer的对象时,Timer实例中会启动一个新的线程(TimerThread),所有的任务都在这个线程中执行,Timer的内部有一个队列用于存储所有的任务。
Timer API:
//下一次任务的执行开始时间是上一次任务开始时间+周期(在没有延时的情况下)
//下一次任务的执行开始时间是上一次任务结束时(在有延时的情况下)
void schedule(TimerTask task, ...)
void scheduleAtFixedRate(TimerTask task, ...)
void cancle()//将任务队列中的所有任务清空,并且线程被销毁
TimerTask API:
void cancle()//将该任务从任务队列中清除
单例的实现:立即加载模式,在调用方法前实例已经被创建:
private static MyObject myObject = new MyObject();
public static MyObject getInstance(){
return myObject;
}
单例的实现:延迟加载,在调用方法时实例才被创建:
//双重检查模式
private static MyObject myObject;
public static MyObject getInstance(){
if(myObject == null){
synchronized(MyObject.class){
if(myObject == null){
myObject = new MyObject();
}
}
}
return myObjcet;
}
单例的实现:静态内部类实现单例:
public class MyObject{
private static class InnerClass{
private static MyObject myObject = new MyObject();
}
public static MyObject getInstance(){
return InnerClass.myObject;
}
}
单例与序列化和反序列化???:
public class MyObject implements Serializable{
private static class InnerClass{
private static MyObject myObject = new MyObject();
}
public static MyObject getInstance(){
return InnerClass.myObject;
}
protected Object readResolve throws ObjectStreamException {
return InnerClass.myObject;
}
}
单例模式:使用静态代码块实现:
public class MyObject{
private static MyObject myObjcet;
static{
myObjcet = new MyObject();
}
public static MyObject getInstance(){
return myObjcet;
}
}
单例模式:使用枚举实现单例:
public enum MyObject {
Instance;
public void doSth(){}
}
//调用
MyObject.Instance.doSth();
线程的状态:
- NEW:尚未启动的线程的状态
- RUNNABLE:正在java虚拟机中执行的线程处于这种状态。
- BLOCKED:受阻塞并等待某个锁的线程处于这种状态。
- WAITING:无期限地等待另一线程执行某一操作的线程处于这种状态。
- TIME_WAITING:有期限地等待另一线程执行某一操作的线程处于这种状态。
- TERMIINATED:退出的线程处于这种状态。
线程组,方便统一管理线程。
当线程组中断时,线程组中的线程都被中断。
SimpleDateFormat是非线程安全的。
线程异常处理:
static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)//静态方法 优先级低
void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)//实例方法 优先级高