线程总结
前言:最近学习了一下底层框架的一些技术,学习完之后需要总结一下,总结的可能不太全面,以后会不断修正知识点,学习用博客来总结自己学习是我的第一步。
———————————————————————————————————
1.
首先我先说一下为什么要是用线程:
我们为什么要用线程,因为合理的利用线程,可以减少开发和维护成本,甚至能改善复杂的应用程序的性能,例子有很多:JAVA虚拟机的垃圾回收机制就是很好例子,这个现在不太明白,以后需要用到就会知道了,需要自己体会总结。
2.
什么是线程呢?
官方解释:被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。 线程简单的理解就是 轻量级进程,多线程就是 同一个应用程序中有多个顺序流同时执行。
3.
怎样去使用,这里我用简单的代码来解释(卖票的一个实例)。
线程有两张方式创建:继承Thread和实现Runnable接口
//继承Thread类
public class Thread_Demo01 extends Thread{
//定义一个私有常量
private int tacket=5;
@Override
public void run() {
for(int i=1;i<=5;i++)
{
if(tacket>0)
{
System.out.println("tacket="+tacket--);
}
}
}
//创建三个线程
public static void main(String[] args) {
new Thread_Demo01().start();
new Thread_Demo01().start();
new Thread_Demo01().start();
}
}
//实现Runnable接口
public class Thread_Demo02 implements Runnable {
private int stacket = 5;
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
//线程同步 ,判断是否有票
synchronized (this) {
if (stacket > 0) {
// 线程休眠_毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// yield线程礼让
if (i == 2) {
System.out.println("线程礼让");
Thread.currentThread().yield();
}
System.out.println(Thread.currentThread().getName()
+ "买票,tacket=" + stacket--);
}
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
Thread_Demo02 t1 = new Thread_Demo02();
// 线程优先级设置
// new Thread(t1).setPriority(Thread.MAX_PRIORITY);
new Thread(t1).start();
new Thread(t1).start();
}
}
4.
观察上面代码你会发现,无论是继承Thread还是实现Runnable接口,都有一个run()方法,线程的运作方式其实是抢占CPU,谁先抢到谁去执行run()方法里的代码片段,抢占方式是随机的。另外你会发现我在实现Runnable接口的列子中用到了其他方法,所以下面我介绍一下继承中经常使用的其他方法:
//返回当前线程
public static Thread currentThread()
//返回线程名字
public final String getName()
//设置线程优先级
public final void setPriority(int priority)
//开始执行线程,使用方式如上
public void start()
//使用目前的线程休眠,使用方式如上
public static void sleep(long m)
//暂停目前的线程,运行其他线程
public final void yield()
//执行线程,此方法并不是真正启动线程,正确方式是调用start()方法
public void run()
---------------------------
//因为是线程内的方法,所以我们在使用的时候是先获取当前线程,再调取线程内的方法,如:
//获取当前线程的名字
Thread.currentThread().getName()
5.
刚才说到线程抢占资源是随机的,但在多线程中也给出了线程的 优先级 设置,如上实例中倒数第三行代码,就是为线程 t1 设置了优先级 ,使用方式如上所示
分为三种:
//最低优先级 表示常量为 1
public static final int MIN_PRIORITY
//中等优先级 表示常量为 5 线程默认优先级
public static final int NORM_PRIORITY
//最高优先级 表示常量为 10
public static final int MAX_PRIORITY
6.
我们线程重不重要,主要看支持线程的地方多不多,下面就要介绍一下 Object类对线程的支持,Object是所有类的父类,所以它的方法使用起来非常方便,线程中所有的对象都可以调用,意义:达到在多线程程序中同步协调资源分配的目的
//线程等待
public final void wait()
//线程等待,并制定等待时间(毫秒)
public final void wait(long timeout)
//唤醒第一个等待线程
public void notify()
//唤醒全部等待的线程
public void notifyAll()
7.
下面我要说的就是线程中最重要的部分了,线程同步,也就是线程安全问题
什么是线程安全呢?举个例子:
一个多个线程程序,(前面介绍过线程执行是随机抢占CPU的)当线程A抢到使用资格后,因为没有同步,在线程A使用过程中,线程B不知道CPU是否空闲,也去使用,这样就会造成混乱,也就是线程安全问题,下面是解决方法
同步方式分为两种,第一种同步代码块,第二种同步方法
同步代码块 在问题3中第二个例子
//两种方法灵活使用
//线程同步
synchronized (同步对象) {
//需要同步的代码块
}
//第二种同步方法
synchronized 方法返回类型 方法名(参数列表){
}
8.
死锁问题
经典示例:(代码太多,就不展示了)
爸爸:想要成绩单
儿子:想要玩具
爸爸对儿子说:把考试成绩单给我,就给你玩具
儿子对爸爸说:把玩具给我,就给你考试成绩单
这个问题陷入死循环 也就是死锁问题
产生条件:
- 互斥条件:一个资源每次只能被一个线程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放
- 不可剥夺条件:进程已获得的资源,在未使用完之前,不可强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要破环死锁4个必要条件中任意一个,死锁问题就能解决
线程的生命周期分别为
- 创建状态:
程序中使用构造方法创建线程对象后,新线程对象即处于创建状态。线程此时已经具有相应的内存空间和其
他资源,但不可运行。 - 就绪状态
线程对象创建后调用start( )方法启动线程,即进入就绪状态。就绪状态下的线程进入线程队列,等待CPU调
用。 - 运行状态
线程获得CPU服务后即处于运行状态,此时将自动调用线程对象的run( )方法。run( )方法定义了该线程的具体操作和实现功能。需要注意的是运行状态下的线程调用yield( )方法后,将从运行状态返回至就绪状态。 - 阻塞状态
运行状态的线程调用sleep( )、wait( )等方法后将进入阻塞状态。线程阻塞条件解除后,线程再次转入就绪状态 - 终止状态
当线程执行run( )方法完毕后处于终止状态(又称死亡状态),处于终止状态下的线程不具有继续运行的能力
以上就是线程的基础知识和运用,随着以后工作中使用线程,会不断的积累线程的使用经验,再慢慢深入。
有不对的地方欢迎指正!