首页 > 代码库 > 黑马程序员_多线程
黑马程序员_多线程
------- android培训、java培训、期待与您交流! ----------
多线程
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方法
线程让步