黑马程序员_多线程
2013-05-17 18:38
260 查看
-------
android培训、java培训、期待与您交流! ----------
1 进程:是一个正在执行的程序
每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制元,一个进程中至少有一个线程
线程:就是进程中的一个独立的控制单元
线程在控制着进程的执行
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程
2 创建线程的第一种方式:继承Thread类
步骤:
1定义类继承Thread类
2复写Thread类中的run方法 目的:将自定义代码存储在run方法中让线程运行
3调用线程是start方法
该方法两个作用:启动线程,调用run方法
发现运行结果每一次都不同
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就执行
明确一点,在某一个时刻,只能有一个程序在运行(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的结果
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权
这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多久,cpu说了算
3 为什么要覆盖run方法呢?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码
4 线程都有自己默认的名称
Thread-编号 编号从零开始
static Thread currentThread(): 获取当前线程对象 Thread.currentThread();
getName();获取线程名称
设置线程名称:setName或者构造函数
5 创建线程的第二种方式:实现Runnable接口
步骤:
1)定义类实现Runnable接口
2)覆盖Runnable接口中的run方法 将线程要运行的代码存放在该run方法中
3)通过Thread类建立线程对象
4)将Runnable接口的子类对象作为实际参数传递给Thread的构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象
所以要让线程去执行指定对象的run方法,就必须明确该run方法所属对象
调用Thread类的start方法开启线程并启用Runnable接口子类的run方法
实现方式和继承方式有什么区别呢?
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable,线程代码存放在接口的子类的run方法中
实现方式的好处:避免了单继承的局限性
在定义线程时,建议使用实现方式
6 如何找出那些代码是需要同步的呢?
明确那些代码是多线程运行代码(run方法里面就是多线程运行代码)
明确共享数据(run方法里面的一般成员变量都是共享数据)
明确多线程运行代码中那些语句是操作共享数据的
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
就是同步代码块:
synchronized(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程即使获取cpu执行权,也进不去,因为没有锁
同步的前提:
1.必须是有两个或者两个以上的线程
2.必须是多个线程使用同一个锁
注意:必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
7 同步函数用的是那一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this
这个例子可以看出,当我们在同步代码块中的锁不是this的时候,程序会有安全隐患,代表同步函数和同步代码块持有的不是同一个锁,当同步代码块中的锁是this的时候,我们发现程序没有安全隐患了,由此我们可以推出,同步函数中的锁是this
如果同步函数被静态修饰后,使用的是什么锁呢?
通过验证,发现不再是this,因为静态方法也不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象 类名.class
8 死锁
同步中嵌套同步,各自持有锁,都不给对方这时候就造成死锁了
9 wait; notify(); notifyAll();
我们这里要注意sleep和wait的区别:
sleep是Threa类的方法而wait是锁里面的方法,有了锁才能调用wait
调用sleep:线程睡一会儿,放弃了cpu执行权,但是没有放开锁,所以锁里的其它线程虽然抢到了cpu执行权,但是都不能进锁里来,知道这个线程睡完觉,出了同步函数
调用wati:让线程等待,线程放弃cpu执行权,放开锁,锁里的其他线程可以进去同步
注意:同步函数里只有有一个活动线程
都使用在同步中,因为要对持有监视器(锁)的线程操作
所以要使用在同步中,因为只能由同步才具有锁
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中
10 join
当a线程执行到了b线程b线程的.join()方法时,a就会等待.等b线程执行完,a才会执行 join可以用来临时加入线程执行
11 那个线程开启你的,你就属于那个组的
优先级设置:t1.setPriority(Thread.MAX_PRIORITY);
释放执行权:Thread.yield();会有中断异常InterrupttedException
作用是尽量的平均cpu的使用权
t1.setDaemon(true);
将该线程标记为守护线程或者运行线程(后台线程),在启动线程前运行
前台线程:所写的线程都是前台线程
当所有前台线程结束后,后台线程自动结束
interrupt中断方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
t1.interrupt();
12 多线程的新特性
android培训、java培训、期待与您交流! ----------
1 进程:是一个正在执行的程序
每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制元,一个进程中至少有一个线程
线程:就是进程中的一个独立的控制单元
线程在控制着进程的执行
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程
2 创建线程的第一种方式:继承Thread类
步骤:
1定义类继承Thread类
2复写Thread类中的run方法 目的:将自定义代码存储在run方法中让线程运行
3调用线程是start方法
该方法两个作用:启动线程,调用run方法
/* 多线程例子1:用继承Thread类的方法创建一个卖票程序 有100张票,4个窗口同时在卖票 */ Class Ticket extends Thread { Private static int ticket=100;//因为票只有100张并且被多个对象调用,所以定义为静态 Public void run()//继承Thread类复写run方法,里面存放多线程要运行的代码 { While(true) { //这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去 Synchronized(Ticket.class) { If(ticket>0) { Try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+”…”+ticket--); } } } } } Class TicketDemo { Public static void main(String[] args) { Ticket t1=new Ticket();//创建线程1对象 t1.start(); Ticket 2=new Tikcet();//创建线程2对象 T2.start(); } }
发现运行结果每一次都不同
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就执行
明确一点,在某一个时刻,只能有一个程序在运行(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的结果
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权
这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多久,cpu说了算
3 为什么要覆盖run方法呢?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码
4 线程都有自己默认的名称
Thread-编号 编号从零开始
static Thread currentThread(): 获取当前线程对象 Thread.currentThread();
getName();获取线程名称
设置线程名称:setName或者构造函数
5 创建线程的第二种方式:实现Runnable接口
步骤:
1)定义类实现Runnable接口
2)覆盖Runnable接口中的run方法 将线程要运行的代码存放在该run方法中
3)通过Thread类建立线程对象
4)将Runnable接口的子类对象作为实际参数传递给Thread的构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象
所以要让线程去执行指定对象的run方法,就必须明确该run方法所属对象
调用Thread类的start方法开启线程并启用Runnable接口子类的run方法
/* 多线程例子2:用实现Runnable方法创建一个卖票程序 有100张票,两个窗口 */ Class Ticket implements Runnable { private int ticket=100; public void run() { while(true) { //这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去 This代码Ticket对象 Synchronized(this) { If(ticket>0) { try{Thread.sleeep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+”…”+ticket--); } } } } } Class Threa2Demo { Public static void main(String[] args) { Ticket t=new Ticket(); Thread t1=new Thread(t); t1.start(); Thread t2= new Thread(t); t2.start(); } }
实现方式和继承方式有什么区别呢?
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable,线程代码存放在接口的子类的run方法中
实现方式的好处:避免了单继承的局限性
在定义线程时,建议使用实现方式
/* 继承方法创建多线程的局限性例子说明: */ class Pson { pivate String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } } //这里学生继承了人,那么如果学生类里面有些代码存在安全隐患要使用多线程,那么就不能使用就不能使用基础Thread类的方法创建多线程了,因为java规定一个类不能有多个父类,所以这种情况下只能用实现Runnable类来创建多线程 class Student extends Person { Student(String name,int age) { super(name,age); } }
6 如何找出那些代码是需要同步的呢?
明确那些代码是多线程运行代码(run方法里面就是多线程运行代码)
明确共享数据(run方法里面的一般成员变量都是共享数据)
明确多线程运行代码中那些语句是操作共享数据的
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
就是同步代码块:
synchronized(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程即使获取cpu执行权,也进不去,因为没有锁
同步的前提:
1.必须是有两个或者两个以上的线程
2.必须是多个线程使用同一个锁
注意:必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
7 同步函数用的是那一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this
/* 多线程例子3: 用实现Runnable方法创建一个卖票程序,Thread0执行同步代码块中的代码,Thread1执行同步函数中的代码,以此来验证同步函数持有的锁是this 有100张票,两个窗口 */ Class Ticket implements Runnable { private int ticket=100; boolean flag=true; public void run() { if(flag) { while(true) { //这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去 This代码Ticket对象 Synchronized(this) { If(ticket>0) { try{Thread.sleeep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+”…”+ticket--); } } } } else while(true) show(); } public void show(); { if(ticket>0) { try{Thread.sleep()}catch(Exception e){} System.out.println(Thread.currentThread().getName()+ticket--); } } } Class Threa2Demo { 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(); } }
这个例子可以看出,当我们在同步代码块中的锁不是this的时候,程序会有安全隐患,代表同步函数和同步代码块持有的不是同一个锁,当同步代码块中的锁是this的时候,我们发现程序没有安全隐患了,由此我们可以推出,同步函数中的锁是this
如果同步函数被静态修饰后,使用的是什么锁呢?
通过验证,发现不再是this,因为静态方法也不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象 类名.class
8 死锁
同步中嵌套同步,各自持有锁,都不给对方这时候就造成死锁了
/* 死锁例子1: 用实现Runnable方法创建一个卖票程序,Thread0执行同步代码块中的代码,Thread1执行同步函数中的代码,以此来验证同步函数持有的锁是this 有100张票,两个窗口 */ Class Ticket implements Runnable { private int ticket=100; boolean flag=true; Object obj=new Object() public void run() { if(flag) { while(true) { //这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去 This代码Ticket对象 Synchronized(obj) { //同步代码块要进去同步函数中执行,持有的锁不同,大家互补想让造成死锁 show(); } } } else while(true) show(); } public void show(); { //同步函数中要进去同步代码块中执行,持有的锁不同,大家互补想让造成死锁 synchronized(obj) { if(ticket>0) { try{Thread.sleep()}catch(Exception e){} System.out.println(Thread.currentThread().getName()+ticket--); } } } } Class Threa2Demo { Public static void main(String[] args) { Ticket t=new Ticket(); Thread t1=new Thread(t); t1.start(); try{Thread.sleep(10);}catch(Exception e){} t.flag=false; Thread t2= new Thread(t); t2.start(); } }
/*死锁例子2 开启两个线程,线程1进来后拿到locka锁打印if locka然后它想执行下面的代码又要拿到lockb的锁,如果线程2拿到lockb的锁不放,那么就形成了死锁 */ class Test Runnable { boolean flag; Test(boolean flag) { this.flag=flag; } if(flag) { synchronized(Lock.locka) { System.out.println(“if locka”); snychronized(Lock.lockb) { System.out.println(“if lockb”); } } } else { synchronized(Lock.lockb) { System.out.println(“else lockb”); synchronized(Lock.locka) { System.out.println(“else locka”); } } } } class Lock { static Lock locka=new Lock(); static Lock lockb=new Lock(); } class TestDemo { public static void main(String[] args) { Test ts1=new Test(true); Test ts2=new Test(false); Thread t1=new Thread(); t1.start(); Thread t2=new Thread(); t2.start(); } }
9 wait; notify(); notifyAll();
我们这里要注意sleep和wait的区别:
sleep是Threa类的方法而wait是锁里面的方法,有了锁才能调用wait
调用sleep:线程睡一会儿,放弃了cpu执行权,但是没有放开锁,所以锁里的其它线程虽然抢到了cpu执行权,但是都不能进锁里来,知道这个线程睡完觉,出了同步函数
调用wati:让线程等待,线程放弃cpu执行权,放开锁,锁里的其他线程可以进去同步
注意:同步函数里只有有一个活动线程
都使用在同步中,因为要对持有监视器(锁)的线程操作
所以要使用在同步中,因为只能由同步才具有锁
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中
/* 生产者和消费者的例子 详解wait; notify(); notifyAll();的用法 */ class Res { private String name; private String sex; private boolean flag=false; public synchronized void setRes(String name,String sex) { //当flag为true时生产者就等待,否则就生产 //这里等待线程醒来之后要判断flag的值,以免造成安全隐患 while(flag) try{this.wait();}catch(Exception e){} this.name=name; this.sex=sex; System.out.println(Thread.currentThread().getName()+"生产者"+name+"..."+sex); flag=true; //这里用notifyAll唤醒所有线程,避免唤醒的是本方线程,注意如果用notify,它唤醒的是进入线程池中的第一个线程,有可能唤醒的是本方线程,造成所有的线程都在等待 this.notifyAll(); } public synchronized void getRes() { //当flag为false时消费者就等待,否则就消费 while(!flag) try{this.wait();}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"消费者"+name+"-----"+sex); flag=false; this.notifyAll(); } } 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.setRes("mike","man"); } r.setRes("丽丽","女"); x=(x+1)%2; } } } class Output implements Runnable { private Res r; Output(Res r) { this.r=r; } public void run() { while(true) r.getRes(); } } 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); Thread t3=new Thread(in); Thread t4=new Thread(out); t1.start(); t2.start(); t3.start(); t4.start(); } }
10 join
当a线程执行到了b线程b线程的.join()方法时,a就会等待.等b线程执行完,a才会执行 join可以用来临时加入线程执行
11 那个线程开启你的,你就属于那个组的
优先级设置:t1.setPriority(Thread.MAX_PRIORITY);
释放执行权:Thread.yield();会有中断异常InterrupttedException
作用是尽量的平均cpu的使用权
t1.setDaemon(true);
将该线程标记为守护线程或者运行线程(后台线程),在启动线程前运行
前台线程:所写的线程都是前台线程
当所有前台线程结束后,后台线程自动结束
interrupt中断方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
t1.interrupt();
/* Interrupt例子 这是一个强制清除冻结状态,让线程重新回到运行状态 */ class StopThread implements Runnable { boolean flag=true; public void run() { while(flag) { try { this.wait(); } catch(InterruptedException e) { System.out.println(Thread.currentThread().getName()+"清除了冻结状态"); flag=false; } System.out.println(Thread.currentThread().getName()+"我来啦"); } System.out.println(Thread.currentThread().getName()+"哈哈,我在外面"); } } class StopDemo { public static void main(String[] args) { StopThread st=new StopThread(); Thread t1=new Thread(st); Thread t2=new Thread(st); t1.start(); t2.start(); //如果这里加上t1.join()主线程碰到t1的join方法,释放了cpu执行权,然后t1和同 就抢夺cpu,最后等t1执行完主线程才会执行,如果t1执行过程中遇到了wait方法时,会产生InterruptedException异常,让t1恢复正常状态 //t1.join(); //如果这里把t1设为后台线程,因为没有设置的线程都默认为前台线程,那么等t2和主线程都运行完,t1就会自动结束 // t1.setDaemon(true); int x=1; while(true) { if(x++==60) { t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+x); } System.out.println(Thread.currentThread().getName()+"哈哈我是主线程"); } }
12 多线程的新特性
/* 生产者和消费者的升级版 java新特性 Lock代替了synchronized函数和代码块,Condition对象代替了同步中的wait,notify,notifyAll方法Condition对象把wait,notify,notifyAll方法封装了 获取锁:Lock lock=new ReentrantLock(); 获取跟锁有关的Condition对象: Condition condition=lock.newCondition(); 注意: 1 Condition中用await,signal和signalAll代替了wait,notify,notifyAll方法 2 以前一个锁只有一个对应的wait,notify,notifyAll方法,现在一个锁里面可以有多个await,signal和signalAll方法,因为一个锁可以获取多个Condition对象,而Condition对象又有对应的方法 condition_in.await();等待的是生产者的线程 condition_out.signal();唤醒消费者的线程 condition_out.await();等待的是消费者的线程 condition_in.await();唤醒生产者的线程 */ import java.util.concurrent.locks.*; class Res3 { private String name; private String sex; private boolean flag=false; private Lock lock=new ReentrantLock(); private Condition condition_in=lock.newCondition(); private Condition condition_out=lock.newCondition(); public void setRes(String name,String sex)throws InterruptedException { lock.lock(); //这里写try{}finally{}是为了避免当发生异常的时候,保证可以线程释放锁 try { while(flag) //让生产者wait condition_in.await(); this.name=name; this.sex=sex; System.out.println(Thread.currentThread().getName()+"生产者"+name+"..."+sex); flag=true; //这里用notifyAll唤醒所有线程,避免唤醒的是本方线程,注意如果用notify,它唤醒的是进入线程 池中的第一个线程,有可能唤醒的是本方线程,造成所有的线程都在等待 //叫醒消费者 condition_out.signal(); } finally { lock.unlock(); } } public void getRes()throws InterruptedException { lock.lock(); try { //当flag为false时消费者就等待,否则就消费 while(!flag) condition_out.await(); System.out.println(Thread.currentThread().getName()+"消费者"+name+"-----"+sex); flag=false; condition_in.signal(); } finally { lock.unlock(); } } } class Input3 implements Runnable { private Res3 r; Input3(Res3 r) { this.r=r; } public void run() { int x=0; while(true) { if(x==0) { try{r.setRes("mike","man");}catch(Exception e){} } try{r.setRes("丽丽","女");}catch(Exception e){} x=(x+1)%2; } } } class Output3 implements Runnable { private Res3 r; Output3(Res3 r) { this.r=r; } public void run() { while(true) try{r.getRes();}catch(Exception e){} } } class InputOutputDemo3 { public static void main(String[] args) { Res3 r=new Res3(); Input3 in=new Input3(r); Output3 out=new Output3(r); Thread t1=new Thread(in); Thread t2=new Thread(in); Thread t3=new Thread(out); Thread t4=new Thread(out); t1.start(); t2.start(); t3.start(); t4.start(); } }
相关文章推荐
- 黑马程序员 -->齐头并进完成任务--多线程(下)
- 黑马程序员ava学习笔记——多线程
- 黑马程序员_学习笔记第11天——多线程1
- 黑马程序员_Java多线程
- 黑马程序员——多线程
- 黑马程序员_多线程(死锁)
- 黑马程序员—多线程的学习(2)
- 黑马程序员_多线程的两种创建方式与区别
- 进程和多线程-黑马程序员
- 黑马程序员----Java多线程
- 31.黑马程序员-多线程(继承Thread)
- 黑马程序员-多线程-12天-2
- 黑马程序员_多线程
- 黑马程序员————java中实现多线程的二种方式
- 黑马程序员——多线程
- 黑马程序员——JAVA基础——多线程、反射
- 黑马程序员--多线程
- 黑马程序员——多线程
- 黑马程序员——java基础——多线程(1)
- 黑马程序员——基础知识总结_多线程