* 创建线程任务两种方式
* 1.继承Thread 类 重写run方法。此方法内容为多线程任务。
* 2.通过d实现Runnable接口实现run方法。创建d对象 Ticket d = new Ticket(); 更常用。
* 创建多线程
* Thread t1 = new Thread(); 方式一
* Thread t2 = new Thread(t); 方式二
* 开启线程
* t1.start();
*
* 线程状态
* 安全问题涉及到两个因素:
* 1,多个线程在操作共享数据。
* 2,有多条语句对共享数据进行运算。
*
* 同步代码块与同步函数
* synchronized(对象) { // 任意对象都可以。这个对象就是锁。需要被同步的代码;}
* 同步函数所使用的锁就是this锁 当同步函数被static修饰时 用的是 (类名.class)
* 拿到锁的线程才能执行代码块中的语句。
字符串常量做锁,常量是唯一的,两个线程跑一个代码块,必须是第一个线程丢锁之后,第二个线程才能拿到锁。
而 new String("常量"),每次执行都是新的锁。
等待唤醒机制:涉及的方法:
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
wait() 方法应该由锁对象来调用,锁是哪个对象就由谁来调用。同步函数用 this.wait 同步代码块用 lock.wait. 静态同步函数中使用EarlyNotify.class.wait();, 因为EarlyNotify.class就是其锁对象
notiry同理。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁,并且需要唤醒。
Sleep:线程会释放执行权,但是不释放锁。
yield() :线程会释放执行权,而且线程会释放锁,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。
// 延迟 执行该线程。 放弃当前的执行权和锁, 进入就绪队列。分到cpu后会再次尝试获得锁,并执行
while (Thread.activeCount()>2){ // main gc
Thread.yield();
}
释放执行权和释放锁的区别。
释放执行权:即将cpu让给其他线程,但仍拥有锁,同步中的其他线程仍不能执行。。
释放锁是针对同步而言,将锁给另一个线程允许其执行同步中的内容。
因而,wait丢失执行权,允许同步中的其他线程执行。
而 , sleep不允许同步中的其他线程执行,但是其他同步中的线程可以执行。
锁排队与锁竞争问题:
开启同步后,该线程执行完以后才会释放锁,但中途可能会丢失执行权,去执行其他代码或其他同步块。
对于同一个同步块,多个线程进行排队,进行锁的竞争。
对于同一个同步方法,调用对象不同,则锁不同,不是同一个同步。(隐式参数不同)
对于两个同步方法,调用对象相同,线程不同,也是同一个锁,在一个同步内。
锁是否相同关键是直接调用同步块方法的对象是否相同。同一个对象的多个同步方法共享一把锁
若该方法式static,即隐式参数相同,则锁为类对象,多个对象调用时是同一把锁,在一个同步内。
中断线程
1. 通过循环中断
2.interrupt():中断线程。
在阻塞状态下调用interrupt 将会抛出 interruptedException异常,并且中断无效。
中断情况下不能调用sleep ,sleep不能中断,中断必须在进行时。中断时调用sleep会清除中断,并抛出中断异常。
setPriority(int newPriority):更改线程的优先级。
getPriority():返回线程的优先级。
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
守护线程
- 守护线程 进程线程(主线程挂了) 守护线程也会被自动销毁.
Thread thread = new Thread()
thread.setDaemon(true);
thread.start();