多线程



现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。




进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在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();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值