这是自己以前学习线程时所做的一些总结
一:创建一个线程
继承Thread 类
线程类(Thread)包含一个可以运行的过程(方法):run()方法
2) 创建一个具体线程的步骤如下:
第一,继承 Thread 类
第二,重写 run 方法(就是更新运行过程), 实现用户自己的过程
第三,创建线程实例(就是创建一个线程)
第四,使用线程实例的 start() 方法启动线程, 启动0以后线程会尽快的去并发执行 run()
线程的 5 中状态
1) New 新建状态
当程序使用 new 关键字创建了一个线程后,该线程就处于新建状态,此时线程还未启动,
当线程对象调用 start()方法时,线程启动,进入 Runnable 状态
2) Runnable 可运行(就绪)状态
当线程处于 Runnable 状态时,表示线程准备就绪,等待获取 CPU
3) Running 运行(正在运行)状态
假如该线程获取了 CPU,则进入 Running 状态,开始执行线程体,即 run()方法中的内
容
注意:
如果系统叧有 1 个 CPU,那么在任意时间点则叧有 1 条线程处于 Running 状态;
如果是双核系统,那么同一时间点会有 2 条线程处于 Running 状态
但是,当线程数大于处理器数时,依然会是多条线程在同一个 CPU 上轮换执行
当一条线程开始运行时,如果它丌是一瞬间完成,那么它丌可能一直处于 Running 状态,
线程在执行过程中会被中断,目的是让其它线程获得执行的机会,像这样线程调度的策
略取决于底层平台。对于抢占式策略的平台而言,系统系统会给每个可执行的线程一小
段时间来处理仸务,当该时间段(时间片)用完,系统会剥夺该线程所占资源(CPU),
让其他线程获得运行机会。
调用 yield()方法,可以使线程由 Running 状态进入 Runnable 状态
4) Block 阻塞(挂起)状态
当如下情况下,线程会进入阻塞状态:
线程调用了 sleep()方法主动放弃所占 CPU 资源
线程调用了一个阻塞式 IO 方法(比如控制台输入方法),在该方法返回前,该线
程被阻塞
当正在执行的线程被阻塞时,其它线程就获得执行机会了。需要注意的是,当阻塞结束
时,该线程将进入 Runnable 状态,而非直接进入 Running 状态
5) Dead 死亡状态
当线程的 run()方法执行结束,线程进入 Dead 状态
需要注意的是,丌要试图对一个已经死亡的线程调用 start()方法,线程死亡后将丌能再次作为
线程执行,系统会抛出 IllegalThreadStateException 异常
注释----
1) new 运算创建线程后,线程进入 New 状态(初始状态)
2) 调用 start()方法后,线程从 New 状态进入 Runnable 状态(就绪状态)
start()方法是在 main()方法(Running 状态)中调用的
3) 线程结束后,进入 Dead 状态(死亡状态),被对象垃圾回收
4) main()方法结束后,其它线程,比如上例中 p1 和 p2 开始抢着进入 Running 状态
由谁抢到是底层操作系统决定(操作系统分配时间片)
单核处理器:在一个时间点上叧有一个线程在 Running 状态;双核处理器:2 个
如果 p1 进入 Running 状态,当操作系统分配给它的时间片到期时,p1 进入 Runnable
状态,p2 进入 Running 状态
在期间有可能其它的进程的线程获得时间片,那么 p1 和 p2 同时进入 Runnable 状态,
等待操作系统分配时间片
5) 线程进入 Dead 状态后,叧能被垃圾回收,丌能再开始
6) 如果线程在运行过程中,自己调用了 yield()方法,则主动由 Running 状态进入 Runnable 状
态
状态管理
1) 让出 CPU Thread.yield()
当前线程让出处理器(离开 Running 状态),使当前线程进入 Runnable 状态等待
2) 休眠 Thread.sleep(times)
使当前线程从 Running 放弃处理器进入 Block 状态, 休眠 times 毫秒, 再返回到 Runnable
如果其他线程打断当前线程的 Block(sleep), 就会发生 InterruptedException。
1) 线程的优先级 (资源紧张时候, 尽可能优先)
t3.setPriority(Thread.MAX_PRIORITY); 设置为最高优先级------------最高级别为10,最低级别为1,默认级别为5
默认有 10 优先级, 优先级高的线程获得执行(进入 Running 状态)的机会多. 机会的
多少丌能通过代码干预
默认的优先级是 5
2) 后台线程(守护线程,精灵线程)-----------------------演示(后台线程设置为循环100次输出,前台循环10次输出)
t1.setDaemon(true);
Java 进程的结束:当前所有前台线程都结束时, Java 进程结束
当前台线程结束时, 丌管后台线程是否结束, 都要被停掉!
3) 获得线程名字
getName()
4) 获得当前线程
Thread main = Thread.currentThread();
实现线程第二种方式(因为java单继承)
实现 Runnable 接口
实现步骤:
实现 Runnable 接口, 实现 run()方法, 提供并发运程的过程
创建这个类的实例, 用这个实例作为 Thread 构造器参数,创建 Thread 类
使用 start() 方法启动线程
演示
还可以使用匿名内部类
Thread t1=new Thread(){
@Override
public void run() {
System.out.println("线程1开始");
}
};
t1.start();
Runnable r=new Runnable() {
@Override
public void run() {
System.out.println("线程2开始");
}
};
Thread t2=new Thread(r);
t2.start();
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程3开始");
}
});
t3.start();
new Thread(){
@Override
public void run() {
System.out.println("线程4开始");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
IO B lock 是由 IO 操作时触发的,和 Sleep Block 相同,Sleep Block 是有 sleep()方法触发的
触发方法如 readLine()、nextLine()、next()、nextInt()等
一般 IO 读取方法都会发生 IO Block
1) 异步
并发, 各干自己的。如: 一群人同时上卡车
2) 同步
步调一致的处理。 如: 一群人排队上公交车
1) 多个线程并发读写同一个临界资源时候会发生”线程并发安全问题“,如果保证多线程同步访
问临界资源,就可以解决。
2) 常见的临界资源:
多线程共享实例变量
静态公共变量
3) 使用同步代码块解决线程并发安全问题
synchronized(同步监视器){
}
同步监视器 是一个任意对象实例. 是一个多个线程之间的互斥的锁机制. 多个线程要使
用同一个"监视器"对象 实现同步互斥
synchronized(this){
}
如果方法的全部过程需要同步, 可以简单使用 synchronized 修饰方法,相当于整个方法
的 synchronized(this)
尽量减少同步范围,提高并发效率
int count=20;
@Override
public void run() {
for(int i=0;i<50;i++){
if(count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+count--+"号票");
}
}
}
main
TicketSouce t=new TicketSouce();
new Thread(t,"t1").start();
new Thread(t,"t2").start();
new Thread(t,"t3").start();
synchronized这个关键字有两种用法1、放方法名前形成同步方法;2、放在块前构成同步块。
(1)同步方法
public synchronized void sale(){
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+count--+"号票");
}
-----运行
synchronized (this) {
if(count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+count--+"号票");
}
}