第一种创建方式:继承Thread类
复写Tread的run()方法,调用start()开启线程并执行
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *Xiancheng2继承Thread类,实现run()方法 3 *main即为主线程 4 *线程随机访问 5 */ 6 class XianCheng2 extends Thread 7 { 8 public void run(){ 9 for(int x=0;x<500;x++) 10 System.out.println("run---"+x); 11 } 12 } 13 class XianCheng1 14 { 15 public static void main(String[] args){ 16 XianCheng2 x=new XianCheng2(); 17 //开启线程并执行 18 x.start(); 19 for(int i=0;i<500;i++){ 20 System.out.println("mian----"+i); 21 } 22 } 23 }
设置和获取线程的名称:构造方法(name),void setName();this.getName(),Thread.currentThread().getName()(标准写法);
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 1 /** 2 2 *需求:1.通过两种方式设置线程的名字 3 3 *需求:2.通过两种方式获取线程的名字 4 4 */ 5 5 class XianCheng2 extends Thread 6 6 { 7 7 //1.1构造方法设置名字 8 8 XianCheng2(String name){ 9 9 super(name); 10 10 } 11 11 public void run(){ 12 12 for(int i=0;i<30;i++) 13 13 //2.1通过getName()方法获取当前线程的名字 14 14 System.out.println(this.getName()+"-----"+i); 15 15 } 16 16 } 17 17 class XianCheng3 extends Thread 18 18 { 19 19 public void run(){ 20 20 for(int i=0;i<30;i++){ 21 21 //2.2通过Thread的静态方法currentThread()获取当前对象的引用,从而获取名字 22 22 System.out.println(XianCheng3.currentThread().getName()+"-----"+i); 23 23 } 24 24 } 25 25 } 26 26 class XianCheng1 27 27 { 28 28 public static void main(String[] args){ 29 29 new XianCheng2("one").start(); 30 30 XianCheng3 x=new XianCheng3(); 31 31 //1.2通过setName()方法设置线程名称 32 32 x.setName("two"); 33 33 x.start(); 34 34 35 35 for(int i=0;i<30;i++) 36 36 //这里不能使用this!! 37 37 System.out.println(Thread.currentThread().getName()+"-----"+i); 38 38 } 39 39 }
思考:如果XianCheng2这个类已经是某个类的子类,还能通过继承的方式创建线程吗?
第二种方式:实现Runnable接口
显然,限制于单继承,XianCheng2无法再继承Thread,现在可以通过实现runnable接口创建线程!
卖票小程序:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 1 /** 2 2 *三个售票员同时卖票,共100张 3 3 */ 4 4 class Ticketer implements Runnable 5 5 { 6 6 private int num=100; 7 7 public void run(){ 8 8 while(num>0){ 9 9 /*下句将会出现问题,出现0票数和-1票数 10 10 当num=1时,假设第一个线程执行到wihle,条件符合,这时候cpu切换到第二个线程,执行到while 11 11 条件也符合,但始终剩下的输出语句还会执行,所以当这种假设出现,0则出现,这是不被期望的 12 12 */ 13 13 //try{Thread.sleep(10);}catch(InterruptedException e){} 14 14 //进程名...票号 15 15 System.out.println(Thread.currentThread().getName()+"..."+num--); 16 16 } 17 17 } 18 18 19 19 } 20 20 class RunnableDemo 21 21 { 22 22 public static void main(String[] args) 23 23 { 24 24 Ticketer t=new Ticketer(); 25 25 //Thread(runnable,name) 26 26 new Thread(t,"T-one").start(); 27 27 new Thread(t,"T-two").start(); 28 28 new Thread(t,"T-three").start(); 29 29 } 30 30 }
注意:当执行上面的程序加上第13行代码的时候,程序就容易出问题了!!当有多个线程操作某个共有数据的时候,一定要警惕,程序时候会出问题!!
思考:如何控制某代码块若被某线程执行,则执行完,其他线程无法执行?
synchronized
作用:加锁
用法1:synchronized(obj),方法体内封装要加锁的代码块,obj参数即为使用锁的对象
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 1 /** 2 2 *三个售票员同时卖票,共100张 3 3 */ 4 4 class Ticketer implements Runnable 5 5 { 6 6 Object obj=new Object();//synchronized(obj) 7 7 private int num=100; 8 8 public void run(){ 9 9 while(num>0){ 10 10 //加锁 11 11 synchronized(obj){ 12 12 if(num>0){ 13 13 try{Thread.sleep(10);}catch(InterruptedException e){} 14 14 System.out.println(Thread.currentThread().getName()+"..."+num--); 15 15 } 16 16 } 17 17 } 18 18 } 19 19 20 20 } 21 21 class RunnableDemo 22 22 { 23 23 public static void main(String[] args) 24 24 { 25 25 Ticketer t=new Ticketer(); 26 26 //Thread(runnable,name) 27 27 new Thread(t,"T-one").start(); 28 28 new Thread(t,"T-two").start(); 29 29 new Thread(t,"T-three").start(); 30 30 } 31 31 }
用法2:作为关键字修饰非静态函数,函数体即为加锁的代码块,此时锁是this
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 1 /** 2 2 *三个售票员同时卖票,共100张 3 3 */ 4 4 class Ticketer implements Runnable 5 5 { 6 6 private int num=100; 7 7 public void run(){ 8 8 while(num>0){ 9 9 show(); 10 10 } 11 11 } 12 12 //修饰方法体,加锁 13 13 public synchronized void show(){ 14 14 if(num>0){ 15 15 try{Thread.sleep(10);}catch(InterruptedException e){} 16 16 System.out.println(Thread.currentThread().getName()+"..."+num--); 17 17 } 18 18 } 19 19 20 20 } 21 21 class RunnableDemo 22 22 { 23 23 public static void main(String[] args) 24 24 { 25 25 Ticketer t=new Ticketer(); 26 26 //Thread(runnable,name) 27 27 new Thread(t,"T-one").start(); 28 28 new Thread(t,"T-two").start(); 29 29 new Thread(t,"T-three").start(); 30 30 } 31 31 }
程序验证是this:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 1 /** 2 2 *两个售票员同时卖票,共100张 3 3 *验证:synchronized修饰的函数用的锁是this 4 4 */ 5 5 class Ticketer implements Runnable 6 6 { 7 7 private int num=100; 8 8 boolean flag=true; 9 9 Object obj=new Object(); 10 10 public void run(){ 11 11 if(flag){ 12 12 while(num>0){ 13 13 synchronized(obj){//当这里传入this时才能正常卖完票,否则0票出现 14 14 if(num>0){ 15 15 try{Thread.sleep(10);}catch(InterruptedException e){} 16 16 System.out.println(Thread.currentThread().getName()+"...hei..."+num--); 17 17 } 18 18 } 19 19 } 20 20 } 21 21 else 22 22 while(num>0) 23 23 show(); 24 24 } 25 25 //修饰方法体,加锁 26 26 public synchronized void show(){ 27 27 if(num>0){ 28 28 try{Thread.sleep(10);}catch(InterruptedException e){} 29 29 System.out.println(Thread.currentThread().getName()+"...hi..."+num--); 30 30 } 31 31 } 32 32 33 33 } 34 34 class RunnableDemo 35 35 { 36 36 public static void main(String[] args) 37 37 { 38 38 Ticketer t=new Ticketer(); 39 39 //Thread(runnable,name) 40 40 new Thread(t,"T-one").start(); 41 41 //让主线程休息10毫秒,这时只有T-one在跑,且flag=true, 42 42 //当执行到while时”根本停不下来“ num不为0的情况下 43 43 try{Thread.sleep(10);}catch(InterruptedException e){} 44 44 //10毫秒过后,主线程醒了,下句执行,falg=false, 45 45 //然后T-one开启,因false,T-one执行else部分,执行到while也停不下来 46 46 //所以两个进程在10毫秒之后就开始不停的切换 47 47 t.flag=false; 48 48 new Thread(t,"T-two").start(); 49 49 } 50 50 }
0票出现:如果是一个锁,那么不会出现0票,然而出现了,说明了函数体的锁不是obj,当synchronized(this)时,正确卖票,此时说明是同一把锁,即函数锁是this!!
为何强调非静态?如果是静态方法,则是类.静态方法(),轮不到this,此时锁是谁?
静态类加载进内存时,不会创建本类对象,唯一的对象就是Class对象-》字节码文件对象:类名.class,而这个锁就是类名.class
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *两个售票员同时卖票,共100张 3 *验证:synchronized修饰的函数用的锁是this 4 */ 5 class Ticketer implements Runnable 6 { 7 private static int num=100; 8 boolean flag=true; 9 public void run(){ 10 if(flag){ 11 while(num>0){ 12 synchronized(Ticketer.class){//传入字节码对象 13 if(num>0){ 14 try{Thread.sleep(10);}catch(InterruptedException e){} 15 System.out.println(Thread.currentThread().getName()+"...hei..."+num--); 16 } 17 } 18 } 19 } 20 else 21 while(num>0) 22 show(); 23 } 24 //修饰方法体,加锁 25 public static synchronized void show(){ 26 if(num>0){ 27 try{Thread.sleep(10);}catch(InterruptedException e){} 28 System.out.println(Thread.currentThread().getName()+"...hi..."+num--); 29 } 30 } 31 32 } 33 34 class StaticSynchronized 35 { 36 public static void main(String[] args) 37 { 38 Ticketer t=new Ticketer(); 39 //Thread(runnable,name) 40 new Thread(t,"T-one").start(); 41 //让主线程休息10毫秒,这时只有T-one在跑,且flag=true, 42 //当执行到while时”根本停不下来“ num不为0的情况下 43 try{Thread.sleep(10);}catch(InterruptedException e){} 44 //10毫秒过后,主线程醒了,下句执行,falg=false, 45 //然后T-one开启,因false,T-one执行else部分,执行到while也停不下来 46 //所以两个进程在10毫秒之后就开始不停的切换 47 t.flag=false; 48 new Thread(t,"T-two").start(); 49 } 50 }
补充单例模式:http://blog.csdn.net/jason0539/article/details/23297037
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 需求:单例模式恶汉式SingleOne与懒汉式SingleTow 3 */ 4 //恶汉式,在类初始化时创建一个唯一的对象 5 class SingleOne 6 { 7 private SingleOne(){} 8 //初始化时创建一个唯一对象 9 private static final SingleOne single=new SingleOne(); 10 //通过静态方法获取这个对象 11 public static SingleOne getInstance(){ 12 return single; 13 } 14 15 } 16 //懒汉式:在类初始化时不创建对象->延迟加载 17 //然而面临多线程时,懒汉式可能会出现问题->不再单例 18 class SingleTwo 19 { 20 private SingleTwo(){} 21 private static SingleTwo single=null; 22 public static SingleTwo getInstance(){ 23 //如果对象还没建立,则同步建立,双重if减少使用锁为1次 24 if(single==null){ 25 synchronized(SingleTwo.class){//注意,这里是静态的,锁是类文件字节码 26 if(single==null) 27 single=new SingleTwo(); 28 } 29 } 30 return single; 31 } 32 }
死锁
在线程级考虑死锁:线程T1拿到锁a的时候想拿锁b,线程T2拿到锁b的时候想拿到锁a,这样他们就锁上了!!
考虑不死锁:在T1拿到a的时候,又继续拿到了b,这时候T2干等着,T1用完之后T2就可以用了
死锁小程序:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 定义两个同步嵌套同步的锁造成死锁 3 */ 4 class DeadLoack implements Runnable 5 { 6 boolean flag=true; 7 DeadLoack(boolean flag){ 8 this.flag=flag; 9 } 10 public void run(){ 11 if(flag) 12 { 13 //得锁a 14 synchronized(Loack.a){ 15 System.out.println(Thread.currentThread().getName()+"..."+"a"); 16 //得锁b 17 synchronized(Loack.b){ 18 System.out.println(Thread.currentThread().getName()+"..."+"a2"); 19 } 20 } 21 } 22 else 23 { 24 //得锁b 25 synchronized(Loack.b){ 26 System.out.println(Thread.currentThread().getName()+"..."+"b"); 27 //得锁a 28 synchronized(Loack.a){ 29 System.out.println(Thread.currentThread().getName()+"..."+"b2"); 30 } 31 } 32 } 33 } 34 } 35 //定义两把锁 36 class Loack 37 { 38 static Object a=new Object(); 39 static Object b=new Object(); 40 } 41 42 class DeadLoackDemo 43 { 44 public static void main(String[] args) 45 { 46 DeadLoack d1=new DeadLoack(true); 47 DeadLoack d2=new DeadLoack(false); 48 new Thread(d1,"one").start(); 49 //加上下句,基本上就不会死锁了,在10毫秒内,线程d1完全可以得到锁b 50 //try{Thread.sleep(10);}catch(Exception e){} 51 new Thread(d2,"two").start(); 52 53 } 54 }
线程之间的通信
以下代码试图:Input对象的信息->Output对象信息 循环执行 试图得到结果{李一男 男}或{liernv girl}
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 试图:线程a输入数据{李一男 0}或{liernv 1},线程b输出数据{李一男 0}或{liernv 1} 3 */ 4 class Resource 5 { 6 String name; 7 int sex; 8 } 9 class Input implements Runnable 10 { 11 Resource r; 12 //通过构造方法传入资源对象的引用,从而实现让多个线程可以共享同一对象 13 Input(Resource r){ 14 this.r=r; 15 } 16 public void run(){ 17 //写 18 r.sex=0; 19 while(true){ 20 if(r.sex==0){ 21 r.name="李一男"; 22 } 23 else{ 24 r.name="liernv"; 25 } 26 r.sex=(r.sex+1)%2; 27 } 28 29 } 30 } 31 class Output implements Runnable 32 { 33 Resource r; 34 Output(Resource r){ 35 this.r=r; 36 } 37 public void run(){ 38 //读 39 while(true){ 40 String x=(r.sex==0)?"男":"girl"; 41 System.out.println(r.name+"..."+x); 42 } 43 } 44 } 45 class Correspondence 46 { 47 public static void main(String[] args) 48 { 49 Resource r=new Resource(); 50 Input in=new Input(r); 51 Output out=new Output(r); 52 new Thread(in).start(); 53 new Thread(out).start(); 54 } 55 }
然而得到的数据并不是说期望的:输出的李一男时男时女。。
分析:假设某时刻状态 liernv girl,此时Input执行了写操作->李一男 girl ,此时还没来的及将girl 写为 男,Output已经开始读:李一男 girl
改进:将读和写操作用同一个同步锁锁起来,那用哪一个锁呢?----最好是用临界资源,即共同操作的对象!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 试图:线程a输入数据{李一男 0}或{liernv 1},线程b输出数据{李一男 0}或{liernv 1} 3 */ 4 class Resource 5 { 6 String name; 7 int sex; 8 } 9 class Input implements Runnable 10 { 11 Resource r; 12 //通过构造方法传入资源对象的引用,从而实现让多个线程可以共享同一对象 13 Input(Resource r){ 14 this.r=r; 15 } 16 public void run(){ 17 //写 18 boolean x=true; 19 while(true){ 20 synchronized(r){//同步锁 21 if(x){ 22 r.name="李一男";r.sex=0;x=false; 23 } 24 else{ 25 r.name="liernv";r.sex=1;x=true; 26 } 27 } 28 } 29 30 } 31 } 32 class Output implements Runnable 33 { 34 Resource r; 35 Output(Resource r){ 36 this.r=r; 37 } 38 public void run(){ 39 //读 40 while(true){ 41 synchronized(r){//同步锁 42 String x=(r.sex==0)?"男":"girl"; 43 System.out.println(r.name+"..."+x); 44 } 45 } 46 } 47 } 48 class Correspondence 49 { 50 public static void main(String[] args) 51 { 52 Resource r=new Resource(); 53 Input in=new Input(r); 54 Output out=new Output(r); 55 new Thread(in).start(); 56 new Thread(out).start(); 57 } 58 }
考虑:做这样一件事情,如果临界资源存在,则不能执行Input,只能执行Output;当临界资源不存在,则只能执行Input,不能执行Output!
多线程-等待唤醒机制
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 需求:Input和Output同步,临界不空则不Input,临界空则不Output 3 */ 4 class Resource 5 { 6 String name; 7 int sex; 8 boolean flag=false; 9 } 10 class Input implements Runnable 11 { 12 Resource r; 13 //通过构造方法传入资源对象的引用,从而实现让多个线程可以共享同一对象 14 Input(Resource r){ 15 this.r=r; 16 } 17 public void run(){ 18 //写 19 boolean x=true; 20 while(true){ 21 synchronized(r){//同步锁 22 //如果有货,则等待 23 if(r.flag) 24 try{r.wait();}catch(Exception e){} 25 //写 26 if(x){ 27 r.name="李一男";r.sex=0;x=false; 28 } 29 else{ 30 r.name="liernv";r.sex=1;x=true; 31 } 32 //change 33 r.flag=true; 34 //唤醒output 35 r.notify(); 36 } 37 } 38 39 } 40 } 41 class Output implements Runnable 42 { 43 Resource r; 44 Output(Resource r){ 45 this.r=r; 46 } 47 public void run(){ 48 //读 49 while(true){ 50 synchronized(r){//同步锁 51 //如果没货,这等待 52 if(!r.flag) 53 try{r.wait();}catch(Exception e){} 54 //读 55 String x=(r.sex==0)?"男":"girl"; 56 System.out.println(r.name+"..."+x); 57 //change 58 r.flag=false; 59 //唤醒input 60 r.notify(); 61 } 62 } 63 } 64 } 65 class Correspondence 66 { 67 public static void main(String[] args) 68 { 69 Resource r=new Resource(); 70 Input in=new Input(r); 71 Output out=new Output(r); 72 new Thread(in).start(); 73 new Thread(out).start(); 74 } 75 }
注意:
1.wait(),notify(),notifyAll()是声明在Object类中的!why?wait和notify是针对同步的,同步必须要锁,锁必须要对象,而wait和notif也必须要指明锁,锁对象可以任意,那只能是在Object中定义
2.在使用wait,notify时,必须指明同步锁对象!(r.wait())?不然我怎么知道哪个线程wait,notify哪个线程!
3.public final void wait()throws InterruptedException,所以需要对异常进行处理!
考虑:写操作和读操作两个方法给谁合适?
代码优化
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 需求:1.资源Resource,具有同步读写方法 3 需求:2.Input和Output实现Runnable接口,启用两个线程 4 */ 5 class Resource 6 { 7 private String name; 8 private String sex; 9 private boolean flag=false; 10 //属性私有,通过set()实现同步写 11 public synchronized void set(String name,String sex){ 12 if(flag) 13 try{this.wait();}catch(Exception e){} 14 this.name=name; 15 this.sex=sex; 16 flag=true; 17 this.notify(); 18 } 19 //通过out()实现同步读 20 public synchronized void out(){ 21 if(!flag) 22 try{this.wait();}catch(Exception e){} 23 System.out.println(name+"..."+sex); 24 flag=false; 25 this.notify(); 26 } 27 } 28 class Input implements Runnable 29 { 30 Resource r; 31 //通过构造方法传入资源对象的引用,从而实现让多个线程可以共享同一对象 32 Input(Resource r){ 33 this.r=r; 34 } 35 public void run(){ 36 //写 37 boolean x=true; 38 while(true){ 39 if(x) 40 { 41 r.set("李一男","男");x=false; 42 } 43 44 else 45 { 46 r.set("Alice","girl");x=true; 47 } 48 } 49 } 50 } 51 class Output implements Runnable 52 { 53 Resource r; 54 Output(Resource r){ 55 this.r=r; 56 } 57 public void run(){ 58 //读 59 while(true) 60 r.out(); 61 } 62 } 63 class Correspondence 64 { 65 public static void main(String[] args) 66 { 67 Resource r=new Resource(); 68 new Thread(new Input(r)).start(); 69 new Thread(new Output(r)).start(); 70 } 71 }
生产者与消费者问题:两个生产者,两个消费者,一个共有资源
解决方式一:利用synchronized加锁,wait()等待,notify(),notifyAll()唤醒
不足:当多个线程时,为了避免全部等待的情况,使用notifyAll()唤醒,生产和消费都被唤醒。然而这不是所期待的,生产唤醒消费,消费唤醒生产就可以了,不必要唤醒自己人!
Demo
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *需求;两个生产者,两个消费者,一个共享资源 3 */ 4 //通过构造传入资源 5 class Producer implements Runnable 6 { 7 Resource r; 8 Producer(Resource r){ 9 this.r=r; 10 } 11 public void run(){ 12 while (true) 13 { 14 r.set("饲料"); 15 } 16 } 17 } 18 //通过构造传入资源 19 class Consumer implements Runnable 20 { 21 Resource r; 22 Consumer(Resource r){ 23 this.r=r; 24 } 25 public void run(){ 26 while (true) 27 { 28 r.out(); 29 } 30 } 31 } 32 class Resource 33 { 34 private String name; 35 private int res=1; 36 private boolean flag=false; 37 //true则生产wait,否则生产-改true-唤醒其他所有线程 38 public synchronized void set(String name){ 39 while(flag) 40 try{this.wait();}catch(Exception e){} 41 this.name=name+".."+res++; 42 System.out.println(Thread.currentThread().getName()+"..生产资源.."+this.name); 43 flag=true; 44 this.notifyAll(); 45 } 46 //false则消费wait,否则消费-改false-唤醒其他所有线程 47 public synchronized void out(){ 48 while(!flag) 49 try{this.wait();}catch(Exception e){} 50 System.out.println(Thread.currentThread().getName()+"......消费资源......"+this.name); 51 flag=false; 52 this.notifyAll(); 53 } 54 55 } 56 class ProducerConsumer 57 { 58 public static void main(String[] args) 59 { 60 //创建资源 61 Resource r=new Resource(); 62 //开启两个消费者和两个生产者线程,共享资源通过构造传入 63 new Thread(new Producer(r),"one").start(); 64 new Thread(new Producer(r),"two").start(); 65 new Thread(new Consumer(r),"three").start(); 66 new Thread(new Consumer(r),"four").start(); 67 68 } 69 }
Lock
解决方式二:利用java.util.concurrent.locks这个包里面的类Lock和Condition。
lock()-unlock()类似synchronized;condition类似加锁对象;condition.await()类似r.wait();conditon.signal(),sondition.signalAll()类似r.notify(),r.notifyAll()
不同的是:一个锁lock可以有多个condition
Demo
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *需求;两个生产者,两个消费者,一个共享资源 3 */ 4 //Lock接口时定义在java.util.concurrent.locks包的 5 import java.util.concurrent.locks.*; 6 class Producer implements Runnable 7 { 8 Resource r; 9 Producer(Resource r){ 10 this.r=r; 11 } 12 public void run(){ 13 while (true) 14 { 15 //为何要try?因为set()throws InterruptedException 16 try{r.set("饲料");}catch(Exception e){} 17 } 18 } 19 } 20 //通过构造传入资源 21 class Consumer implements Runnable 22 { 23 Resource r; 24 Consumer(Resource r){ 25 this.r=r; 26 } 27 public void run(){ 28 while (true) 29 { 30 31 try{r.out();}catch(Exception e){} 32 } 33 } 34 } 35 class Resource 36 { 37 private String name; 38 private int res=1; 39 private boolean flag=false; 40 //注意,Lock是定义的一个接口,不能实例化的,ReentrantLock是实现了Lock的类 41 //创建锁lock,四个线程都用这个锁 42 Lock lock =new ReentrantLock(); 43 //创建lock的两个环境,分别是生产对象和消费对象 44 Condition pro=lock.newCondition(); 45 Condition con=lock.newCondition(); 46 //为什么要抛?因为 void await() throw InterruptedException 47 public void set(String name)throws InterruptedException { 48 lock.lock(); 49 try 50 { 51 while(flag) 52 pro.await(); 53 this.name=name+".."+res++; 54 System.out.println(Thread.currentThread().getName()+"..生产资源.."+this.name); 55 flag=true; 56 //只唤醒一个消费者线程!! 57 con.signal(); 58 } 59 finally 60 { 61 lock.unlock(); 62 } 63 } 64 //false则消费wait,否则消费-改false-唤醒其他所有线程 65 public void out()throws InterruptedException { 66 lock.lock(); 67 try 68 { 69 while(!flag) 70 con.await(); 71 System.out.println(Thread.currentThread().getName()+"......消费资源......"+this.name); 72 flag=false; 73 //只唤醒一个生产者线程 74 pro.signal(); 75 } 76 finally 77 { 78 lock.unlock(); 79 } 80 } 81 82 } 83 class ProducerConsumer 84 { 85 public static void main(String[] args) 86 { 87 //创建资源 88 Resource r=new Resource(); 89 //开启两个消费者和两个生产者线程,共享资源通过构造传入 90 new Thread(new Producer(r),"one").start(); 91 new Thread(new Producer(r),"two").start(); 92 new Thread(new Consumer(r),"three").start(); 93 new Thread(new Consumer(r),"four").start(); 94 95 } 96 }
注意:
1.Lock:在java.util.concurrent.locks包中,是一个接口,ReentrantLock是一个实现了该接口的一个类
所以创建锁时 Lock lock=new ReentrantLock();是合法的
2.使用锁
... method()throws InterruptedException {//调用时还得处理异常 lock.lock();//锁开始 try { ... conditiona.await();//就是这玩意儿抛异常 ... conditionb.signal(); } finally//必须的,不然发生异常时,就不能解锁了 { lock.unlock();//锁结束 } }
中断public void interrupt()
情景:假设总共有主线程和一个子线程在执行,子线程冻结了(wait ,await),主线程执行完了且未唤醒子线程,此时整个程序就停下来了,且不会结束子线程,因为子线程的run()方法没有执行完成!!
interrupt():清除线程的冻结状态
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *需求: 3 *一个主线程,一个子线程,让主线程执行完且子线程冻结 4 *尝试用interrupt()清除子线程的冻结状态 5 */ 6 class InterruptDemo 7 { 8 public static void main(String[] args) 9 { 10 int count=100; 11 Interrupt in=new Interrupt(); 12 Thread t=new Thread(in,"child"); 13 t.start(); 14 while(count>0){ 15 System.out.println(Thread.currentThread().getName()+"..."+count--); 16 } 17 System.out.println("man is over"); 18 in.changeFlag(); 19 //清除冻结状态 20 //t.interrupt(); 21 } 22 } 23 class Interrupt implements Runnable 24 { 25 private boolean flag=true; 26 public synchronized void run(){ 27 while (flag) 28 { 29 try 30 { 31 this.wait(); 32 } 33 catch (Exception e) 34 { 35 System.out.println(Thread.currentThread().getName()+"..."+"Exception"); 36 } 37 System.out.println(Thread.currentThread().getName()+"..."+"run"); 38 } 39 } 40 public void changeFlag(){ 41 flag=false; 42 } 43 }
public final void join() throws InterruptedException
作用:抢夺(主线程?)执行权,让主线程等待join进来的线程执行完毕。
待解决:何时抛出中断异常
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //join 强制抢夺执行权,所以可能异常 2 /** 3 *主线程和一个子线程执行,加入第二个子线程,夺取正在执行的线程的执行权 4 */ 5 class JoinDemo 6 { 7 public static void main(String[] args) throws InterruptedException 8 { 9 int num=100; 10 Join j=new Join(); 11 //注意,线程t1和t2均传入j,这样就共同操作j这个对象了---线程通信 12 Thread t1=new Thread(j,".....one"); 13 Thread t2=new Thread(j,"two"); 14 t1.start();//t1线程开启 15 t1.join();//此时是main在执行t1.join,main要等到t1完成才会继续执行 16 t2.start();//t1执行完了,t2才开启 17 18 19 while(num>0) 20 System.out.println("main........."+num--); 21 System.out.println("over"); 22 } 23 } 24 class Join implements Runnable 25 { 26 private int num=100; 27 public synchronized void run(){ 28 while(num>0) 29 System.out.println(Thread.currentThread().getName()+"..."+num--); 30 System.out.println(Thread.currentThread().getName()+".........is..........over"); 31 } 32 }
守护线程(后台线程)public final void setDaemon(boolean on)
?:线程可分为前台线程和后台线程,当所有前台线程都结束后,剩下的后台线程立刻停止,程序结束。
注意:将线程设置成守护线程,要在线程开启之前。且要慎用!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 *测试:两个守护子线程等待,主线程执行完毕,程序时候结束 3 */ 4 class DaemonDemo 5 { 6 public static void main(String[] args) 7 { 8 9 Daemon d1=new Daemon(); 10 Thread t1=new Thread(d1,"one"); 11 Thread t2=new Thread(d1,"two"); 12 int num=60; 13 //在启动之前设置守护线程,守护线程就是后台线程 14 //当所有的前台线程执行完时,剩余的后台线程将会停止 15 t1.setDaemon(true); 16 t2.setDaemon(true); 17 t1.start(); 18 t2.start(); 19 while(true){ 20 if(num==0){ 21 System.out.println("man is over"); 22 break; 23 } 24 System.out.println("main......"+num--); 25 } 26 27 } 28 } 29 class Daemon implements Runnable 30 { 31 32 private boolean flag=true; 33 //不加synchronized会抛出异常 34 public synchronized void run(){ 35 while(flag) 36 try 37 { 38 this.wait(); 39 } 40 catch (InterruptedException e) 41 { 42 System.out.println("wait fof exception"); 43 } 44 } 45 }
线程优先级(1,5,10)与匿名内部类开启线程
1 /**
2 需求:查看和设置线程的优先级 toString setPriority
3 需求:yield让线程暂时释放执行权
4 需求:使用两种匿名内部类的方式开启线程
5 //问题:在使用Thread匿名内部类的时候,如何给线程命名?->I get it!
6 */
7 class ThreadDemo
8 {
9 public static void main(String[] args)
10 {
11 //Thread匿名内部类
12 new Thread("one"){
13 public void run(){
14 for(int i=0;i<50;i++){
15 System.out.println(this.toString()+"...."+i);
16 //释放
17 Thread.yield();
18 }
19 }
20 }.start();
21 //Runnable匿名内部类
22 Runnable r=new Runnable(){
23 public void run(){
24 for(int i=0;i<50;i++){
25 //这里不能用this,否则调用的是Object原始的toString
26 System.out.println(Thread.currentThread().toString()+"...."+i);
27 //释放
28 Thread.yield();
29 }
30 }
31 };
32 new Thread(r,"two").start();
33 //优先级1,5,10,学着:public static final int MAX_PRIORITY=10;用静态定义常量
34 Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
35 for(int i=0;i<50;i++){
36 //这里不能用this,否则调用的是Object原始的toString
37 System.out.println(Thread.currentThread().toString()+"...."+i);
38 }
39 }
40 }