学习Java之day19
1.线程的生命周期
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五 种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束线程的生命周期
1.问题:买票的过程中,出现了重票,错票,--->出现了线程的安全问题。 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程也参与进来,操作车票 3.如何解决:当一个线程a进行对ticket操作时,其他线程不能参与进来。直到线程a操作完ticket时, 其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能改变。
4.在Java中,我们通过同步机制,来解决线程安全的问题。 方式一:同步代码块 synchronized (同步监视器){ //需要被同步的代码 }
说明:1.操作共享数据的代码,即为需要被同步的代码。---->不能包含太多的代码了,也不能包含太少的代码了。
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3.同步监视器:俗称为锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要公用同一把锁。
补充:在实现Runnable接口的创建线程的方式中,可以使用this来充当锁。
方式二:同步代码块
如果需要操作共享数据的代码声明在一个方法中,我们不妨将此方法声明同步的。
5.同步的方式,解决了线程的安全问题----->好处 操作同步代码时,只能有一个线程操作,其他的线程只能是等待。相当于是单线程的过程,效率低。---局限性
使用同步方法解决实现Runnable接口的线程安全问题
关于同步方法的总结:
-
同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
-
非静态的同步方法,同步监视器是:this 静态的同步方法,同步监视器是:当前类本身
3. 使用同步机制将单例模式中的懒汉式改为线程安全的。
class Bank {
private Bank() {
}
private static Bank instance = null;
public static Bank getInstance() {
//效率稍差
// synchronized (Bank.class) {
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
4.死锁的演示
class A {
public synchronized void foo(B b) { //同步监视器:A类的对象:a
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了A实例的foo方法"); // ①
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用B实例的last方法"); // ③
b.last();
}
public synchronized void last() {//同步监视器:A类的对象:a
System.out.println("进入了A类的last方法内部");
}
}
class B {
public synchronized void bar(A a) {//同步监视器:b
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了B实例的bar方法"); // ②
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用A实例的last方法"); // ④
a.last();
}
public synchronized void last() {//同步监视器:b
System.out.println("进入了B类的last方法内部");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
Thread.currentThread().setName("主线程");
// 调用a对象的foo方法
a.foo(b);
System.out.println("进入了主线程之后");
}
public void run() {
Thread.currentThread().setName("副线程");
// 调用b对象的bar方法
b.bar(a);
System.out.println("进入了副线程之后");
}
public static void main(String[] args) {
DeadLock dl =