黑马程序员—多线程

来源:互联网 发布:淘宝店铺突然没有访客 编辑:程序博客网 时间: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中的waitnotify 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 (垃圾回收器),它就是一个很称职的守护者。

 

UserDaemon两者几乎没有区别:

唯一的不同之处就在于虚拟机的离开:如果 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>、期待与您交流! -------

0 0
原创粉丝点击