首页 > 安全资讯 >

多线程

17-01-24

多线程:现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间。

多线程:现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

“同时”执行是人的感觉,在线程之间实际上轮换执行。

使用线程:

一:创建一个线程

继承Thread 类

线程类(Thread)包含一个可以运行的过程(方法):run()方法

创建一个具体线程的步骤如下:

第一,继承 Thread 类

第二,覆盖 run 方法(就是更新运行过程), 实现用户自己的过程

第三,创建线程实例(就是创建一个线程)

第四,使用线程实例的 start() 方法启劢线程, 启劢以后线程会尽快的去并发执行

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),

让其他线程获得运行机会。

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 状

图片说明:

睡眠---调用线程的sleep方法

等待--调用Object的wait方法

挂起--调用yeid方法-线程显示让出CPU控制权

阻塞-例如输出输入IO事件

状态管理

1) 让出 CPU Thread.yield()

当前线程让出处理器(离开 Running 状态),使当前线程迚入 Runnable 状态等待

2) 休眠 Thread.sleep(times)

使当前线程从 Running 放弃处理器迚入 Block 状态, 休眠 times 毫秒, 再返回到 Runnable如果其他线程打断当前线程的 Block(sleep), 就会发生 InterruptedException。

线程的优先级 (资源紧张时候, 尽可能优先)

 t3.setPriority(Thread.MAX_PRIORITY); 设置为最高优先级------------最高级别为10,最低级别为1,默认级别为5

 默认有 10 优先级, 优先级高的线程获得执行(迚入 Running 状态)的机会多. 机会的

多少不能通过代码干预

 默认的优先级是 5

@Override

public void run() {

// TODO Auto-generated method stub

Thread.currentThread().setPriority(1);

for (int i = 0; i < 100; i++) {

System.out.println("你是谁"+i);

}

}

守护线程 /精灵线程/后台线程

--任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

后台线程(守护线程,精灵线程)-----------------------演示(后台线程设置为循环100次输出,前台循环10次输出)

 t1.setDaemon(true);

 Java 迚程的结束:当前所有前台线程都结束时, Java 迚程结束

 当前台线程结束时, 不管后台线程是否结束, 都要被停掉!

Thread2 t2=new Thread2();

t2.setDaemon(true);

join

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行。

Thread1 t1=new Thread1();

Thread2 t2=new Thread2();

t1.setName("主线程");

t1.start();

t1.join();

t2.start();

在这个实验中,我们发现这次线程运行是先把t1运行完毕之后再运行的t2从某种意义上来说,可以使线程同步起来

案例2:Thread2 t2=new Thread2(); 这个实验我们也发现了同样的事情

try {

t2.start();

t2.join();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

for(int i=0;i<10;i++){

System.out.println(Thread.currentThread().getName()+" "+i);

}

实现多线程第二种方式,实现接口

在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比

继承Thread类有如下好处:

->避免单继承的局限,一个类可以继承多个接口。

->适合于资源的共享

实验1:经典的卖票

public class MyThread extends Thread {

private int ticket=10;

public void run(){

for(int i=0;i<20;i++){

if(this.ticket>0){

System.out.println(getName()+"出售车票"+this.ticket--);

}

}

}

}

MyThread myThread1=new MyThread();

myThread1.setName("窗口1");

MyThread myThread2=new MyThread();

myThread2.setName("窗口2");

MyThread myThread3=new MyThread();

myThread3.setName("窗口3");

myThread1.start();

myThread2.start();

myThread3.start();

我们会发现每个窗口都出售了10张票,这很恐怖

我们使用runnable接口来做这个事情

public class MyThread implements Runnable {

private int ticket=10;

public void run(){

for(int i=0;i<20;i++){

if(this.ticket>0){

System.out.println(Thread.currentThread().getName()+"出售车票"+this.ticket--);

}

}

}

}

MyThread myThread=new MyThread();

new Thread(myThread,"窗口1").start();

new Thread(myThread,"窗口2").start();

new Thread(myThread,"窗口3").start();

通过实验,我们发现在这里我们达到了数据共享

同步代码锁

补充01:

1) 异步

并发, 各干自己的。如: 一群人同时上卡车

2) 同步

步调一致的处理。 如: 一群人排队上公交车

多个线程并发读写同一个临界资源时候会发生”线程并发安全问题“,如果保证多线程同步访

问临界资源,就可以解决。

2) 常见的临界资源:

 多线程共享实例变量

 静态公共变量

3) 使用同步代码块解决线程并发安全问题

 synchronized(同步监视器){

}

 同步监视器 是一个仸意对象实例. 是一个多个线程乊间的互斥的锁机制. 多个线程要使

用同一个"监视器"对象 实现同步互斥

synchronized(this){

}

如果方法的全部过程需要同步, 可以简单使用 synchronized 修饰方法,相当于整个方法的

、 synchronized(this)

尽量减少同步范围,提高并发效率

---------------------------------------------------------------

同步方法:---要使用临界资源的位置加锁

int count = 20;

@Override

public void run() {

for (int i = 0; i < 50; i++) {

sale();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

public synchronized void sale() {

if (count > 0) {

System.out.println(Thread.currentThread().getName() + "号窗口卖出" + count-- + "号票");

}

}

如果去掉修饰符--这里多测试几次会发现有相同的票出现

---------------------------------------------------------------

方式2:同步代码块的方式:

int count = 20;

@Override

public void run() {

for (int i = 0; i < 50; i++) {

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-- + "号票");

}

}

}

}

TicketSouce t=new TicketSouce();

new Thread(t,"t1").start();

new Thread(t,"t2").start();

new Thread(t,"t3").start();
相关文章
最新文章
热点推荐