黑马程序员—多线程
来源:互联网 发布:淘宝店铺突然没有访客 编辑:程序博客网 时间:2024/06/10 06:31
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
多线程:
1. 线程
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。一个进程中至少有一个线程。
多线程:
Java VM 启动的时候会有一个进程java.exe.该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
1,如何在自定义的代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述。
有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。
创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
为什么要覆盖run方法呢?
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
如:
class Demo extends Thread{public void run(){for(int x=0; x<60; x++)System.out.println("demo run----"+x);}} class ThreadDemo {public static void main(String[] args) {//for(int x=0; x<4000; x++)//System.out.println("Hello World!"); Demo d = new Demo();//创建好一个线程。d.start();//开启线程并执行该线程的run方法。(运行结果是D线程与主线程争夺资源)//d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。(只运行主线程)for(int x=0; x<60; x++)System.out.println("Hello World!--"+x);}}
2. 线程运行状态
创建线程后可能会挂起,睡眠的线程唤醒后可能会挂起;
一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。
被创建:等待启动,调用start启动。
运行状态:具有执行资格和执行权。
临时状态(阻塞):有执行资格,但是没有执行权。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
消忙状态:stop()方法,或者run方法结束。
注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。
3. 获取线程对象和名称
练习:
创建两个线程,和主线程交替运行。
线程都有自己默认的名称。
Thread-编号 该编号从0开始。
static Thread currentThread():获取当前线程对象。
getName(): 获取线程名称。
设置线程名称:setName或者构造函数。
class Test extends Thread{Test(String name){super(name);//构造函数自定义线程名称}public void run(){for(int x=0; x<60; x++){System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);}} } class ThreadTest {public static void main(String[] args) {Test t1 = new Test("one---");Test t2 = new Test("two+++");t1.start();t2.start();//t1.run();//t2.run(); for(int x=0; x<60; x++){System.out.println("main....."+x);}}}
4. 卖票程序+创建线程第2种方式
创建线程的第二种方式:实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去执行指定对象的run方法。Thread类就必须明确该run方法所属对象。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法中。
请看如下案例:
/*
需求:简单的卖票程序。
多个窗口同时买票。
*/
class Ticket implements Runnable//extends Thread{private int tick = 100;//用Static可以实现多个线程共享一个数据,但是Static的声明周期太长;public void run(){while(true){if(tick>0){try{Thread.sleep(10);}catch(Exception e){};//用于查看负数的tickSystem.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);}}}} class TicketDemo{public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);//创建了一个线程;Thread t2 = new Thread(t);//创建了一个线程;Thread t3 = new Thread(t);//创建了一个线程;Thread t4 = new Thread(t);//创建了一个线程;t1.start();t2.start();t3.start();t4.start();}} 案例2/* * 实现多线程,在线程中输出线程名称,每隔300毫秒输出一次,输出20次 */class test2 {public static void sop(Object obj){System.out.println(obj);}public static void main(String[] args) {printThread print = new printThread();for(int i=0;i<20;i++){new Thread(print,"线程"+i).start();}}}class printThread implements Runnable{public void run(){synchronized(this){System.out.println(Thread.currentThread().getName());try{Thread.sleep(300);}catch (InterruptedException e){}}}}
5. 多线程的安全问题+同步代码块
买票程序中出现错误数字:0,-1,-2的原因
通过分析,发现,打印出0,-1,-2等错票。
多线程的运行出现了安全问题。
多线程出现安全问题的原因?
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性 。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。同步
同步代码块。
格式
synchronized(对象)
{
需要被同步的代码
}
有共享数据的代码块才加锁;
锁是对象,又称监视器
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
=========================================
火车上的卫生间---经典。
==========================================
同步的前提:
1,必须要有两个或者两个以上的线程。(都加上同步才满足)
2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
未满足这两个条件,不能称其为同步。
好处:解决了多线程的安全问题。
弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
/*给卖票程序实例加上同步代码块*/
class Ticket implements Runnable{private int tick = 100;Object obj = new Object();public void run(){while(true){synchronized(obj){if(tick>0){//try{Thread.sleep(10);}catch(Exception e){}//目的是分析执行过程System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);}}}}} class TicketDemo2{public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();t2.start();t3.start();t4.start();}}
6. 同步函数
有线程的程序如何找问题(需要加锁的代码块):
1,明确哪些代码是多线程运行代码。
2,明确共享数据(用的是同一个锁)。
3,明确多线程运行代码中哪些语句是操作共享数据的。
同步有两种形式:
1.同步代码块;
2.同步函数;
/*
需求:
银行有一个金库。
有两个储户分别存300员,每次存100,存3次。
目的:该程序是否有安全问题,如果有,如何解决?
*/
class Bank{private int sum;//Object obj = new Object();public synchronized void add(int n){//synchronized(obj)//{sum = sum + n;try{Thread.sleep(10);}catch(Exception e){}System.out.println("sum="+sum);//}}} class Cus implements Runnable{private Bank b = new Bank();public void run(){for(int x=0; x<3; x++){b.add(100);}}} class BankDemo{public static void main(String[] args) {Cus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}
7. 多线程-同步函数的锁是this
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。
当两个同步语句不是同一个锁时(对象),会出现错误数字0...;
通过下面程序进行验证。
/*
使用两个线程来买票。
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。
*/
class Ticket implements Runnable{private int tick = 100;Object obj = new Object();boolean flag = true;public void run()//如果锁加到这里,只有一个线程使用tick{if(flag){while(true){synchronized(this)//因为使用了this锁,且锁要一致。所以用this{if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);}}}}elsewhile(true)show();}public synchronized void show()//this锁{if(tick>0){try{Thread.sleep(10);}catch(Exception e){}//目的是分析运行过程System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);}}} class ThisLockDemo{public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();//t1线程有了执行资格后瞬间,t.flag = false,t2.start();可能全执行完;导致flag=false,t1线程没有执行try{Thread.sleep(10);}catch(Exception e){}//所以先让主线程睡一会,使其能执行t1同步代码块;t.flag = false;//醒来后flag=false,执行同步函数t2.start();}}
8. 多线程-静态同步函数的锁是class对象
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
请看如下案例:
class Ticket implements Runnable{private static int tick = 100;//Object obj = new Object();boolean flag = true;public void run(){if(flag){while(true){synchronized(Ticket.class){if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);}}}}elsewhile(true)show();}public static synchronized void show(){if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);}}} class StaticMethodDemo{public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start(); }}
9. (必记)多线程-单例设计模式-懒汉式
/*
单例设计模式。
判断锁会耗费一些资源
*/
//饿汉式。
/*
class Single{private static final Single s = new Single();private Single(){}public static Single getInstance(){return s;}}
*/
//懒汉式,一般不用,面试常遇到(请写一个延迟加载的单例设计模式示例?)
特点:实例的延迟加载
缺点:多线程访问时会出现安全问题
解决方案:加同步,如果是同步函数或单一判断的同步代码块,会降低效率,用双重判断形式(双检索形式),解决效率问题;
使用的锁是哪个?
该类所属的字节码文件对象
class Single{private static Single s = null;private Single(){} public static Single getInstance()//如果是同步函数,多线程每次都要判断一下,降低效率,所以不用{if(s==null)//双重判断,判断锁的次数减少,提高效率{synchronized(Single.class)//没有上面的IF语句的话,同样每次都要判断一下,没效率。{if(s==null)//--->A;s = new Single();}}return s;}} class SingleDemo {public static void main(String[] args) {Single s1 = Single.getInstance();}}
10. 多线程-死锁
死锁举例
/*
死锁。
同步中嵌套同步。
*/
class Ticket implements Runnable{private int tick = 1000;Object obj = new Object();boolean flag = true;public void run(){if(flag){while(true){synchronized(obj)//0线程有OBJ锁,{show();//0线程要this锁}}}elsewhile(true)show();}public synchronized void show()//1线程有this锁{synchronized(obj)//1线程要obj锁{if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);}}}} class DeadLockDemo{public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start(); }}
面试题:请写一个死锁?经典代码
class Test implements Runnable{//根据判断标记来执行run方法中不同的方法块private boolean flag;Test(boolean flag){this.flag = flag;} public void run(){if(flag){while(true)//为了执行多次,让他循环一下,无特殊意义{synchronized(MyLock.locka)//一个线程拿到了a锁{System.out.println(Thread.currentThread().getName()+"...if locka ");synchronized(MyLock.lockb)//拿到了a锁后想拿b锁{System.out.println(Thread.currentThread().getName()+"..if lockb");}}}}else{while(true){synchronized(MyLock.lockb)//一个线程拿到了b锁{System.out.println(Thread.currentThread().getName()+"..else lockb");synchronized(MyLock.locka)//拿到了b锁后想拿a锁{System.out.println(Thread.currentThread().getName()+".....else locka");}}}}}} class MyLock{static Object locka = new Object();static Object lockb = new Object();} class test2{public static void main(String[] args) {//t1拿a锁,想拿b锁Thread t1 = new Thread(new Test(true));//t2拿b锁,想拿a锁Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}
11. 线程间通信-示例代码(不安全)
安全问题原因分析图
/*
线程间通讯:
其实就是多个线程在操作同一个资源,
但是操作的动作不同。
*/
class Res{String name;String sex;} class Input implements Runnable{/*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r*/private Res r ;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){if(x==0){r.name="mike";r.sex="man";}else{r.name="丽丽";r.sex = "女女女女女";}x = (x+1)%2;//目的是轮流写入mike和丽丽}}} class Output implements Runnable{/*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r*/private Res r ;Output(Res r){this.r = r;}public void run(){while(true){System.out.println(r.name+"...."+r.sex);}}}} class InputOutputDemo{public static void main(String[] args) {Res r = new Res(); Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out); t1.start();t2.start();}}
产生问题:两个线程互相抢资源,没有加锁
12.线程间通信-解决安全问题
当加了同步还是出错,就要考虑是否满足同步的前提了。
此例子中,要想使用同一个锁,代码块的锁(obj)不是同一个锁,同步函数的锁(this)也不可以,
类文件.class对象时唯一的,可以,但是牵强
最好是:共享资源对象 r
请看如下案例:
class Res{String name;String sex;} class Input implements Runnable{/*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r*/private Res r ;Input(Res r){this.r = r;}public void run()//函数里面有些不需要同步所以不能用同步函数,成了单线程{int x = 0;while(true){synchronized(r){ if(x==0){r.name="mike";r.sex="man";}else{r.name="丽丽";r.sex = "女女女女女";}x = (x+1)%2;}}}} class Output implements Runnable{private Res r ;Output(Res r){this.r = r;}public void run(){while(true){synchronized(r){System.out.println(r.name+"...."+r.sex);}}}} class InputOutputDemo{public static void main(String[] args) {Res r = new Res();//唯一对象,用作锁 Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out); t1.start();t2.start();}}
13.线程间通信-等待唤醒机制
产生问题:连续的input,连续的output
解决方法:利用等待唤醒机制,限制线程的执行资格,执行权
wait(),notify(),notifyAll();方法等都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
class Res{String name;String sex;boolean flag = false;} class Input implements Runnable{ 线程运行时会创建线程池,wait的线程在线程池中,notify()唤醒的是线程池中的线程,多个等待线程,唤醒的是第一个等待的线程; */private Res r ;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){synchronized(r){ if(r.flag)//返回true才执行下一条语句 try{r.wait();}catch(Exception e){}//wait方法声明异常,但是run方法是接口方法,不能声明,所以直接try处理。//r.wait(): wait()的是持有r这个锁的线程 if(x==0){r.name="mike";r.sex="man";}else{r.name="丽丽";r.sex = "女女女女女";}x = (x+1)%2;r.flag = true;r.notify();//input在wait前,唤醒output}}}} class Output implements Runnable{private Res r ;Output(Res r){this.r = r;}public void run(){while(true){synchronized(r){if(!r.flag)try{r.wait();}catch(Exception e){}System.out.println(r.name+"...."+r.sex);r.flag = false;r.notify();}}}} class InputOutputDemo{public static void main(String[] args) {Res r = new Res(); Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out); t1.start();t2.start();}}
14.线程间通信-代码优化
/*
线程间通讯:
其实就是多个线程在操作同一个资源,
但是操作的动作不同。
*/
class Res{private String name;private String sex;private boolean flag = false; public synchronized void set(String name,String sex){if(flag)//只判断一次try{this.wait();}catch(Exception e){}this.name = name;this.sex = sex;flag = true;this.notify();}public synchronized void out(){if(!flag)try{this.wait();}catch(Exception e){}System.out.println(name+"........"+sex);flag = false;this.notify();}} class Input implements Runnable{private Res r ;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){if(x==0)r.set("mike","man");elser.set("丽丽","女女女女女");x = (x+1)%2;//}}} class Output implements Runnable{private Res r ;Output(Res r){this.r = r;}public void run(){while(true){r.out();}}} class InputOutputDemo2{public static void main(String[] args) {Res r = new Res();//用匿名对象优化代码new Thread(new Input(r)).start();new Thread(new Output(r)).start();/*Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out); t1.start();t2.start();*/}}
15.线程间通信-生产者消费者(多线程处理)
class ProducerConsumerDemo {public static void main(String[] args) {Resource r = new Resource(); Producer pro = new Producer(r);Consumer con = new Consumer(r); Thread t1 = new Thread(pro);Thread t2 = new Thread(pro);Thread t3 = new Thread(con);Thread t4 = new Thread(con); t1.start();t2.start();t3.start();t4.start(); }}
/*
现象:多线程程序(超过两个线程)用if判断,notify()唤醒,出现了生产一次消费两次的情况;
对于 多个 生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断flag标记。
不用while,用if唤醒的线程不会再次判断了。
但是会全部wait,冻结。所以用notifyall()方法唤醒全部;
为什么定义notifyAll,
因为需要唤醒 对方 线程。
因为只用notify,唤醒的是第一个等待的线程,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
*/
class Resource{private String name;private int count = 1;private boolean flag = false;// 生产者: t1 t2public synchronized void set(String name){//if(flag)只判断一次,线程唤醒后不判断flagwhile(flag)//用循环,当线程唤醒后会再次判断flag能判断多次try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);flag = true;this.notifyAll();//notify()方法只唤醒一个线程,} // t3 t4 public synchronized void out(){while(!flag)try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);flag = false;this.notifyAll();}} class Producer implements Runnable{private Resource res; Producer(Resource res){this.res = res;}public void run(){while(true){res.set("+商品+");}}} class Consumer implements Runnable{private Resource res; Consumer(Resource res){this.res = res;}public void run(){while(true){res.out();}}}
遗留问题:
把本方不需要的线程也唤醒了
16.线程间通信-生产者消费者JDK5.0升级版
常规开发思想
import java.util.concurrent.locks.*; class ProducerConsumerDemo2 {public static void main(String[] args) {Resource r = new Resource(); Producer pro = new Producer(r);Consumer con = new Consumer(r); Thread t1 = new Thread(pro);Thread t2 = new Thread(pro);Thread t3 = new Thread(con);Thread t4 = new Thread(con); t1.start();t2.start();t3.start();t4.start(); }}
/*
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized(隐藏)替换成显示Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以通过Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作,不会唤醒本方。
======================================================
1.4版本一个锁对应一个wait,notify,要多个notify...就要重建同步,形成嵌套,容易死锁;
1.5以上,一个锁有好几组wait()等,对应多个condition对象;
======================================================
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
ReentrantLock:一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
Condition newCondition() : 返回用来与此 Lock 实例一起使用的 Condition 实例。
signal():唤醒一个等待线程。
InterruptedException:当线程在活动之前或活动期间处于正在等待、休眠或占用状态且该线程被中断时,抛出该异常。
*/
class Resource{private String name;private int count = 1;private boolean flag = false;// t1 t2private Lock lock = new ReentrantLock();//一个锁可以有多个相关的conditionprivate Condition condition_pro = lock.newCondition();private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException{lock.lock();try{while(flag)condition_pro.await();//t1,t2this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);flag = true;condition_con.signal();}finally{lock.unlock();//释放锁的动作一定要执行。}} // t3 t4 public void out()throws InterruptedException{lock.lock();try{while(!flag)condition_con.await();System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);flag = false;condition_pro.signal();}finally{lock.unlock();}}} class Producer implements Runnable{private Resource res; Producer(Resource res){this.res = res;}public void run(){while(true){try{res.set("+商品+");}catch (InterruptedException e){}}}} class Consumer implements Runnable{private Resource res; Consumer(Resource res){this.res = res;}public void run(){while(true){try{res.out();}catch (InterruptedException e){}}}}
17.停止线程stop()-中断Interrupt()
如何停止线程?
stop方法已经过时。
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环让循环结束,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。
interrupt()中断,即挂起:指强制将冻结状态清除,让你恢复到运行状态
//相当于催眠师把你催眠,别人一砖头把你拍醒,可是你受伤(中断异常)了 。
sleep(),wait()等都能中断。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。但是没有结束,可以操作标记让线程结束。
Thread类提供该方法 interrupt();
哪些情况可以终止当前线程的运行?A,B,D
A、抛出一个例外时。
B、当该线程调用sleep()方法时。
C、当创建一个新线程时。
D、当一个优先级高的线程进入就绪状态时。
请看如下案例:
class StopThread implements Runnable{private boolean flag =true;public void run(){while(flag){try{wait();//t1,t2线程都冻结,只执行主线程。当使用interrupt()方法时,就会捕捉异常。线程恢复执行,但是没有结束,需要操作标记flag为false才能结束}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"....Exception");flag=false;}System.out.println(Thread.currentThread().getName()+"....run");}}public void changeFlag(){flag = false;}} class StopThreadDemo{public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.start();t2.start();int num = 0; while(true){if(num++ == 60){//st.changeFlag();t1.interrupt();//清除冻结后,设置flag=false;t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"......."+num);}System.out.println("over");}}
18.守护线程- setDaemon(true)
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:
前台线程全停了,后台线程(守护线程)才自动停止,接着jvm退出;(圣斗士是守护线程,雅典娜是前台线程)
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
User和Daemon两者几乎没有区别:
唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。
请看如下案例:
class StopThread implements Runnable{private boolean flag =true;public void run(){while(flag){System.out.println(Thread.currentThread().getName()+"....run");}}public void changeFlag(){flag = false;}} class StopThreadDemo{public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st); //main是主线程t1.setDaemon(true);//守护线程t2.setDaemon(true);t1.start();t2.start(); int num = 0; while(true){if(num++ == 60){//st.changeFlag();//t1.interrupt();//t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"......."+num);}System.out.println("over");}}
19.Join()方法
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join()可以用来临时加入线程执行。
class Demo implements Runnable{public void run(){for(int x=0; x<70; x++){System.out.println(Thread.currentThread().toString()+"....."+x);}}} class JoinDemo{public static void main(String[] args) throws Exception{Demo d = new Demo();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();t2.start(); t1.join();//主线程冻结,只等join()的线程t1运行完才执行,不管t2线程。 for(int x=0; x<80; x++){System.out.println("main....."+x);}System.out.println("over");}}
20.优先级&yield方法
/*
线程的优先级:线程抢资源的频率
三级:MAX_PRIORITY--10
MIN_PRIORITY--5默认
NORM_PRIORITY--1
yield:暂停当前正在执行的线程对象,并执行其他线程;
稍微减缓某一线程的运行次数,达到线程类似平均运行的效果
当某一个线程优先级比较高时,它就会抢占其他线程的资源,导致其他线程没有资源可用,会造成阻塞,直到那个优先级高地线程使用完。
*/
class Demo implements Runnable{public void run(){for(int x=0; x<70; x++){System.out.println(Thread.currentThread().toString()+"....."+x);Thread.yield();}}} class JoinDemo{public static void main(String[] args) throws Exception{Demo d = new Demo();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();//t1.setPriority(Thread.MAX_PRIORITY); t2.start(); //t1.join();//主线程只等join()的线程 for(int x=0; x<80; x++){//System.out.println("main....."+x);}System.out.println("over");}}
21.开发程序中写线程
什么时候使用 多 线程?
当某些代码需要同时被执行时,就用单独的线程封装;
可以封装出几个类(较麻烦),或是几个匿名Thread对象,或是Runnable对象;
class ThreadTest {public static void main(String[] args) {new Thread(){public void run(){for(int x=0; x<100; x++){System.out.println(Thread.currentThread().getName()+"....."+x);}}}.start(); for(int x=0; x<100; x++){System.out.println(Thread.currentThread().getName()+"....."+x);} Runnable r = new Runnable(){public void run(){for(int x=0; x<100; x++){System.out.println(Thread.currentThread().getName()+"....."+x);}}};new Thread(r).start(); //new Test1().start();}}/*class Test1 extends Thread{public void run(){for(int x=0; x<100; x++){System.out.println(Thread.currentThread().getName()+"....."+x);}}}
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 黑马程序员—多线程
- 【ShawnZhang】带你看数据结构——第二课:线性表顺序结构
- CSDN新版博客排名规则公示
- uva 1025 - A Spy in the Metro
- 几个比较好的博客
- 一万个不知道大问题
- 黑马程序员—多线程
- ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061)
- [新手入门] mac终端terminal 命令大全介绍
- POJ 2443:Set Operation 经典位运算好题
- Linux字符设备驱动总结程序(二)
- 使用ubuntu 的技巧
- 总结别人的一点操作系统知识,分页分段,死锁,内存碎片,
- ubuntu系统下wireshark普通用户抓包设置
- iOS自动布局之通过代码添加约束