黑马程序员_多线程
多线程
1、进程:
正在运行的程序所占有的内存空间,叫做进程。
线程:
一个应用程序中的子程序,对于CPU,子程序可以有一条独立的执行路径,称为线程。
线程特点:依靠一个应用程序,对于其他子程序,独立被CPU执行的
2、多线程的好处:
充分利用CPU的资源,程序的同时运行,提高效率
3、java中线程创建的两种方法:
第一种:
定义类继承Thread class extends Thread
重写Thread中的run方法;public void run()
创建Thread类的子类对象
调用子类对象的start方法,开启线程
注:start方法告诉JVM开启一个对CPU的执行路径,调用run方法。
class A extends Thread {
//..
public void run() {
// . . .
}
}
A t = new A();
t.start();
第二种(重点):
定义类实现Runnable接口
重写run方法
创建Thread类对象
调用Thread对象的start方法开启线程
class A implements Runnable {
public void run() {
. . .
}
}
Thread t=new Thread(new A());
t.start();
这两种方法的区别:
第一种是继承,具有局限性,数据也是线程独享的;
第二种是实现接口,避免了单继承的局限性,同时数据是线程共享的。
4、线程名字的获取和设置
获取Thread子类的线程名字:
Thread类的方法 String getName();
获取非Thread子类的线程名字:
Thread类的方法 static Thread currentThread();//返回当前正在执行的线程对象
设置线程名字的方法:
Thread类的方法 void setName(字符串的名字)
利用Thread类的构造方法 Thread(字符串名字)
5、线程的状态图(重点)
冻结和阻塞的区别:
冻结值线程的休眠或者等待,此时线程主动的放弃CPU的执行资格,同时也放弃了CPU的执行权力;
阻塞时线程不会放弃CPU的执行资格,但没有CPU的执行权力。
6、同步机制,保证多线程在操作共享数据的安全性
同步代码块:
格式: synchronized(对象){
线程操作的共享数据
}
对象:专业名词,对象监视器,锁
同步方法:
同步的非静态方法的锁是this,表示本类对象的引用
同步的静态方法的锁是本类.class(因为JVM为每一种数据类型赋予了一个静态的成员变量,变量的返回值就是本类的class文件对象。)
7、多线程并发的单例模式懒汉(重点)
懒汉,延迟加载,多线程并发实验,单例模式不能保证对象的唯一性。
1 class single{ 2 private Single(){} 3 private Single s=null; 4 5 public static Single getInstance(){ 6 if(s==null){ 7 synchronized(Single.class){ 8 if(s==null) 9 s=new Single() 10 } 11 } 12 return s; 13 } 14 }
注:面试的时候,写同步代码块,不要写同步方法,进行两次判断提高程序的效率
8、死锁:(重点)
多线程在争抢同一把锁的时候,造成的程序的假死现象。实际的开发中不可能出现
1 class Dead implements Runnable{ 2 private boolean flag; 3 Dead(){} 4 Dead (boolean flag){this.flag=flag;} 5 public void run(){ 6 while (true){ 7 //如果flag=true,就先进入A房间,再进入B房间 8 if(flag){ 9 synchronized (A .lockA){ 10 System.out.println("先进入房间A"); 11 synchronized(B. lockB){ 12 System.out.println("再进入房间B"); 13 } 14 } 15 }else{ 16 synchronized (B.lockB){ 17 System.out.println("先进入房间B"); 18 synchronized(A.lockA){ 19 System.out.println("再进入房间B"); 20 } 21 } 22 } 23 } 24 } 25 26 } 27 28 //建立房间A的锁 29 class A{ 30 public static final A lockA=new A(); 31 } 32 //建立B房间的锁 33 class B{ 34 public static final B lockB=new B(); 35 } 36 public class SiSuo { 37 public static void main(String[] args) { 38 Thread t1=new Thread(new Dead(true)); 39 Thread t2=new Thread(new Dead(false)); 40 t1.start(); 41 t2.start(); 42 43 } 44 }
9、线程通信:
案例一:输入一个名字,打印一个名字。
案例中碰到的问题:
A、数据错乱问题:第二次赋值还没完成,就输出了,导致了性别错乱。
解决方法: 同步共享数据。
B、数据的安全问题还没解决:
只有2个原因:共享数据是否都同步了
同步中的锁是同一个吗
解决方法: 使用Resource的对象作为锁。
C、没有交替的输出:
原因:线程发生了阻塞,cpu资源被其他的线程抢走了
解决方法:定义一个标记,使用等待和唤醒控制线程
D、出现了监视器异常:
原因:wait()和notify()如果不标明谁调用的话,就会是Object类调用的。而作为锁的对象是Resource类的对象。
解决方法:用Resource调用wait()和notify()方法。
1 public class XianCheng { 2 public static void main(String[] args) { 3 Resource r=new Resource(); 4 5 Thread tin=new Thread(new Input(r)); 6 Thread tout=new Thread(new Output(r)); 7 tin.start(); 8 tout.start(); 9 } 10 } 11 class Resource{ 12 String name; 13 String sex; 14 boolean flag=false;//flag为真代表赋值完成,等待,唤醒输出;为假,代表输出完成,等待,唤醒输入; 15 16 Resource (){} 17 } 18 class Input implements Runnable{ 19 Resource r; 20 21 Input (){} 22 Input(Resource r){ 23 this.r=r; 24 } 25 26 public void run(){ 27 int x=0; 28 while(true){ 29 synchronized(r){ 30 if(r.flag==true){ 31 try{ 32 r.wait(); 33 }catch(Exception e){ } 34 } 35 if(x%2==0){ 36 r.name="妖姬"; 37 r.sex="女"; 38 }else{ 39 r.name="亚索"; 40 r.sex="男"; 41 } 42 x++; 43 r.flag=true; 44 r.notify(); 45 } 46 } 47 } 48 } 49 class Output implements Runnable{ 50 Resource r; 51 Output (){} 52 Output(Resource r){ 53 this.r=r; 54 } 55 public void run(){ 56 while(true){ 57 synchronized (r){ 58 if(r.flag==false){ 59 try{ 60 r.wait(); 61 }catch(Exception e){ 62 } 63 } 64 System.out.println(r.name+"..."+r.sex); 65 r.flag=false; 66 r.notify(); 67 } 68 } 69 } 70 }
10、案例二:有一个产品,生产一个消费一个。生产四个,消费四个。
遇到的问题:
A、生产了多个,消费了多个
原因:notify()会唤醒先等待的线程,不管标记此时是什么值。
解决方法:全部唤醒后,再进行标记判断。
B、资源浪费
原因:唤醒了全部进程
1 /* * 实现多生产和多消费 2 * 定义产品对象 -- 生产 -- 消费 3 * 生产者线程,控制生产 4 * 消费者线程,控制消费 5 */ 6 //定义产品类 7 class Product { 8 private String name;//品名 9 private int count = 0 ;//计数器 10 private boolean b = false; 11 //生产方法 12 public synchronized void set(String name){ 13 while(b){ 14 try{this.wait();}catch(Exception e){} 15 } 16 this.name = name +".."+count++;//this.name成员变量,品名+计数器 17 System.out.println(Thread.currentThread().getName()+" 生产第.."+this.name); 18 b = true; 19 this.notifyAll(); 20 } 21 //消费方法 22 public synchronized void get(){ 23 while(!b){ 24 try{this.wait();}catch(Exception e){} 25 } 26 System.out.println(Thread.currentThread().getName()+" 消费第......"+this.name); 27 b = false; 28 this.notifyAll(); 29 } 30 } 31 //定义生产者线程 32 class Pro implements Runnable{ 33 private Product p ; 34 Pro(Product p){this.p = p;} 35 public void run(){ 36 while(true) 37 p.set("黄金"); 38 } 39 } 40 //定义消费线程 41 class Cus implements Runnable{ 42 private Product p ; 43 Cus(Product p ){this.p = p;} 44 public void run(){ 45 while(true) 46 p.get(); 47 } 48 } 49 public class ThreadDemo1 { 50 public static void main(String[] args) { 51 Product p = new Product(); 52 Pro pro = new Pro(p); 53 Cus cus = new Cus(p); 54 Thread t0 = new Thread(pro); 55 Thread t1 = new Thread(pro); 56 Thread t2 = new Thread(pro); 57 Thread t3 = new Thread(pro); 58 59 Thread t4 = new Thread(cus); 60 Thread t5 = new Thread(cus); 61 Thread t6 = new Thread(cus); 62 Thread t7 = new Thread(cus); 63 64 t0.start(); 65 t1.start(); 66 t2.start(); 67 t3.start(); 68 t4.start(); 69 t5.start(); 70 t6.start(); 71 t7.start(); 72 } 73 }
1.5后新的锁
1、java.util.concurrent.locks包中Lock接口
替换了以前的同步机制,获得了更广泛和灵活的锁定操作
2、Lock接口中的方法:
Lock() 获得锁
unlock()释放锁
synchronized(this){等同于获取锁
}等同于释放锁
Lock接口的实现类ReentrantLock
Condition接口
1、java.util.concurrent.locks包中Condition接口
解决了同步没有后,锁也没有了,不能使用notify和wait方法的问题。
2、Condition中的方法:
await()==wait();
signal()==notifty();
signalAll()==notify();
3、实现多线程通信中只唤醒一个线程的步骤
A、导包:java.util.concurrent.locks.*;
B、获取锁:创建Lock接口的实现类对象
Lock lock new ReentrantLock();
C、获取Condition对象,对线程分组管理
lock.new condition()
D、利用Condition对象中的方法await和signal实现等待和唤醒
1 /* 2 * 生产者与消费案例,改造成JDK1.5的新特性,Lock锁方式 3 * 实现节约资源,目的唤醒对方的一个 4 */ 5 import java.util.concurrent.locks.*; 6 class Product { 7 private String name;//品名 8 private int count = 0 ;//计数器 9 private boolean b = false; 10 11 //获取Lock接口的实现类对象,获取锁 12 private Lock lock = new ReentrantLock(); 13 private Condition pro = lock.newCondition(); 14 private Condition cus = lock.newCondition(); 15 16 //生产方法 17 public void set(String name){ 18 lock.lock(); 19 while(b){ 20 try{pro.await();}catch(Exception e){} 21 } 22 this.name = name +".."+count++;//this.name成员变量,品名+计数器 23 System.out.println(Thread.currentThread().getName()+" 生产第.."+this.name); 24 b = true; 25 cus.signal(); 26 lock.unlock(); 27 } 28 //消费方法 29 public void get(){ 30 lock.lock(); 31 while(!b){ 32 try{cus.await();}catch(Exception e){} 33 } 34 System.out.println(Thread.currentThread().getName()+" 消费第......"+this.name); 35 b = false; 36 pro.signal(); 37 lock.unlock(); 38 } 39 } 40 //定义生产者线程 41 class Pro implements Runnable{ 42 private Product p ; 43 Pro(Product p){this.p = p;} 44 public void run(){ 45 while(true) 46 p.set("黄金"); 47 } 48 } 49 //定义消费线程 50 class Cus implements Runnable{ 51 private Product p ; 52 Cus(Product p ){this.p = p;} 53 public void run(){ 54 while(true) 55 p.get(); 56 } 57 } 58 public class ThreadLock { 59 public static void main(String[] args) { 60 Product p = new Product(); 61 Pro pro = new Pro(p); 62 Cus cus = new Cus(p); 63 Thread t0 = new Thread(pro); 64 Thread t1 = new Thread(pro); 65 Thread t2 = new Thread(pro); 66 Thread t3 = new Thread(pro); 67 68 Thread t4 = new Thread(cus); 69 Thread t5 = new Thread(cus); 70 Thread t6 = new Thread(cus); 71 Thread t7 = new Thread(cus); 72 73 t0.start(); 74 t1.start(); 75 t2.start(); 76 t3.start(); 77 t4.start(); 78 t5.start(); 79 t6.start(); 80 t7.start(); 81 } 82 }
线程的停止方法
Thread类中的stop()过时了,所以通过终止run方法来停止线程
方法一:改变循环变量(等待中的线程停不下来)
方法二:利用wait()方法的异常,在catch语句中改变循环变量
Thread中中断线程的方法:void interrupt();
线程的守护
Thread中void setDaemon(boolean on)如果传递布尔值为true,就将调用该方法的线程标记为守护线程,守卫别的线程。
定时任务
java.util.Timer
Timer类的构造方法 Timer(boolean isDaemon)
Timer类中的方法:Schedule(执行的代码TimerTask task,开始时间,持续时间)
执行的代码:task要实现TimerTask接口需重写run方法
Thread中的toString()方法
toString()获取线程的名字,优先级和组
优先级 : 默认5 最低1 最高 10
设置优先级的方法:setPriority(int newPriority)
Thread中的join方法
作用:等待该线程终止
Thread中yield方法
线程让步