黑马程序员--多线程2
2013-09-22 01:57
246 查看
----------------------------------------------------android培训 java培训
期待与您交流-----------------------------------------------------
1.进程:是一个正在执行中的程序;每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
2.线程:进程中的一个独立的控制单元,线程在控制着进程的执行,线程是程序运行是最小单位
一个进程可以有多个线程在运行,一个进程中最少有一个线程在运行
实现多线程的方式:
第一种方式:继承Thread类,并复写run()方法
1.继承Thread方法
2.复写run()方法
3.开启线程
示例:
第二种方式:实现Runnable接口,实现run()方法
1.实现Runnabel接口,复写run方法
2.创建Thread对象
3.开启线程
示例:
继承和实现的区别:
实现Runnable接口的方式避免了继承的单继承的局限性
3.多线程同步问题
同步的原因:当有多个线程共用一个数据时,当一个线程对共享还没操作完进,另一个线程参与进来,导致共享数据的错误
解决办法:当一个线程参与进来操作数据时,不允许其它的线程参与操作,只有当这个线程操作完成后其它线程才可操作
同步的前提:
必须有二个或二个以上的线程参与操作数据
线程必须使用同一把锁
同步的利弊:
解决了多线程的安全问题
多个线程需要判断锁,较为浪费资源
示例:
线程同步:
当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。
达到此目的的过程叫做同步(synchronization)
示例:
4.线程死锁问题:
死锁产生的原因:
当一个同步中嵌套另一个同步时就有可能产生死锁
当A同步中嵌套B同步,而B同步中嵌套A同步,这种情况就有可能产生死锁问题
当A持有锁并向B申请锁时,此时B持有自己的锁并向A申请a锁,二者都在互相向对方要锁
此时程序就会停止不动
示例:
5.线程间通信问题:
线程状态:
线程状态总的可分为五大状态:分别是生、死、可运行、运行、等待/阻塞。
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,
线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。
这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:
线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回
到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,
它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法
会抛出java.lang.IllegalThreadStateException异常。
Thread.sleep(long millis):
Thread.sleep(long millis, int nanos):
静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,
在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
join():Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。
interrupt():中断线程
线程交互:
void notify():唤醒在此对象监视器上等待的单个线程。
void notifyAll(): 唤醒在此对象监视器上等待的所有线程。
void wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法
必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,
除非它拥有那个对象的锁。wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,
每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。
从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,
则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。
sleep()与wait()方法的区别:
sleep():线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。
因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。
sleep()不释放同步锁,释放执行权
wait():释放执行权,释放同步锁
示例:
6.锁的升级
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait(),notify(), notifyAll(),替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock(): 上锁
unlock():解锁
newCondition():创建Condition()对象
Condition:替代了Object中的wait(), notify(), notifyAll()
await():等待
signal():唤醒某个线程
signalAll():唤醒所有线程
示例:
期待与您交流-----------------------------------------------------
1.进程:是一个正在执行中的程序;每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
2.线程:进程中的一个独立的控制单元,线程在控制着进程的执行,线程是程序运行是最小单位
一个进程可以有多个线程在运行,一个进程中最少有一个线程在运行
实现多线程的方式:
第一种方式:继承Thread类,并复写run()方法
1.继承Thread方法
2.复写run()方法
3.开启线程
示例:
class Test extends Thread{ int ticket=100; public void run(){ for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+"====="+ticket--); } } public class ThreadTest{ public static void main(String[] args){ //这并不是在卖100张票,而是在卖200张票,因为建立了二个对象,而每个对象都有个ticket变量,而不是共用一个ticket Test t=new Test(); Test t1=new Test(); //t.run():这只是调用run()方法,并不是开启线程 //开启线程,只能线程开启之后才是多线程,不然就只有main一个主线程在运行 t.start(); t1.start(); //运行时会出现异常,一个线程只能开启一次,开启多次是非法的 //t1.start(); /* for(int i=0;i<50;i++) System.out.println(Thread.currentThread().getName()+"+++++"+i); */ } }
第二种方式:实现Runnable接口,实现run()方法
1.实现Runnabel接口,复写run方法
2.创建Thread对象
3.开启线程
示例:
class Test1 implements Runnable{ static int ticket=100; public void run(){ for(int i=0;i<100;i++){ if(ticket>0) System.out.println(Thread.currentThread().getName()+"====="+ticket--); } } } public class ThreadTest1{ public static void main(String[] args){ Test1 t=new Test1(); //将实现Runnable接口的子类对象传入Thread,创建线程对象 Thread th=new Thread(t); //开启线程,并调用Runnable接口中的run方法 th.start(); Thread th1=new Thread(t); th1.start(); } }
继承和实现的区别:
实现Runnable接口的方式避免了继承的单继承的局限性
3.多线程同步问题
同步的原因:当有多个线程共用一个数据时,当一个线程对共享还没操作完进,另一个线程参与进来,导致共享数据的错误
解决办法:当一个线程参与进来操作数据时,不允许其它的线程参与操作,只有当这个线程操作完成后其它线程才可操作
同步的前提:
必须有二个或二个以上的线程参与操作数据
线程必须使用同一把锁
同步的利弊:
解决了多线程的安全问题
多个线程需要判断锁,较为浪费资源
示例:
//线程共享数据产生的问题 class Ticket2 implements Runnable{ private String name; private int num=100; Ticket2(String name){ this.name=name; } public void show(){ if(num>0) System.out.println(Thread.currentThread().getName()+"--"+"Ticket:"+num--); } public void run(){ while(true){ show(); } } } public class TestTicket2{ /* 这个程序创建了三个线程,但三个线程共享一个任务对象;这样卖出的可以表示是种票 但这样仍存在不安全因素;因为线程是的执行是随机切换的,如还有一张票的时候,这时线程1刚好做 完if判断线程切换到线程2执行,这时线程2仍能仍能进入if语句里,因为线程1还没有执行num--操 作,num还是在于0的;这样当线程1再次执行时,num就为成了1,当线程2再次执行时就输出了0,这 样就将0张票给卖出去了,显然卖出0张票是不合理的 */ public static void main(String[] args){ Ticket2 t=new Ticket2("tt"); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); } }
线程同步:
当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。
达到此目的的过程叫做同步(synchronization)
示例:
class Test2 implements Runnable{ static int ticket=500; Object obj=new Object(); public void run(){ //要想同步代码块同步使用的必须是同一把锁,如果不是同一把锁那么synchronized中代码仍然不会同步 //Object obj=new Object();如果这句代码放在这里,那就表示每创建一个线程就会创建一把锁,多个线程使用的不是同 //一把锁,同步代码中的数据仍不会同步 while(true){ synchronized(obj){ if(ticket>0){ try{ Thread.sleep(10); }catch(Exception e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"====="+ticket--); } } //show(); //sellTicket(); } } //同步函数使用的锁是对象本身this public synchronized void show(){ if(ticket>0){ try{ Thread.sleep(10); }catch(Exception e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"====="+ticket--); } } //静态同步函数使用的锁是对应类的.class文件 public static synchronized void sellTicket(){ if(ticket>0){ try{ Thread.sleep(10); }catch(Exception e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"====="+ticket--); } } } public class ThreadTest2{ public static void main(String[] args){ Test2 t=new Test2(); Thread th=new Thread(t); th.start(); Thread th1=new Thread(t); th1.start(); Thread th2=new Thread(t); th2.start(); } }
4.线程死锁问题:
死锁产生的原因:
当一个同步中嵌套另一个同步时就有可能产生死锁
当A同步中嵌套B同步,而B同步中嵌套A同步,这种情况就有可能产生死锁问题
当A持有锁并向B申请锁时,此时B持有自己的锁并向A申请a锁,二者都在互相向对方要锁
此时程序就会停止不动
示例:
class Lock{ static Object Locka=new Object(); static Object Lockb=new Object(); } class LockTest implements Runnable{ boolean flag; LockTest(boolean flag){ this.flag=flag; } public void run(){ if(flag){ while(true){ //B同步中嵌套A同步 synchronized(Lock.Lockb){ System.out.println(Thread.currentThread().getName()+"------Lockbbbbbbbb"); synchronized(Lock.Locka){ System.out.println(Thread.currentThread().getName()+"++++++Lockaaaa"); } } } }else{ while(true){ //A同步中嵌套B同步 synchronized(Lock.Locka){ System.out.println(Thread.currentThread().getName()+"+++++Lockaaaaaa"); synchronized(Lock.Lockb){ System.out.println(Thread.currentThread().getName()+"-----Lockbbbbb"); } } } } } } public class DeadLockTest{ public static void main(String[] args){ LockTest t=new LockTest(true); LockTest t1=new LockTest(false); Thread th=new Thread(t); th.start(); Thread th1=new Thread(t1); th1.start(); } }
5.线程间通信问题:
线程状态:
线程状态总的可分为五大状态:分别是生、死、可运行、运行、等待/阻塞。
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,
线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。
这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:
线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回
到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,
它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法
会抛出java.lang.IllegalThreadStateException异常。
Thread.sleep(long millis):
Thread.sleep(long millis, int nanos):
静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,
在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
join():Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。
interrupt():中断线程
线程交互:
void notify():唤醒在此对象监视器上等待的单个线程。
void notifyAll(): 唤醒在此对象监视器上等待的所有线程。
void wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法
必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,
除非它拥有那个对象的锁。wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,
每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。
从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,
则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。
sleep()与wait()方法的区别:
sleep():线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。
因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。
sleep()不释放同步锁,释放执行权
wait():释放执行权,释放同步锁
示例:
class Resource{ String name; int age; boolean flag=false; Resource(){} Resource(String name,int age){ this.name=name; this.age=age; } } class Input implements Runnable{ Resource s=null; Input(Resource s){ this.s=s; } public void run(){ int x=0; while(true) synchronized(s){ if(s.flag) //每个锁只能对应一个wait(),notity(),notifyAll();如果synchronized()中的锁和代码中的锁不一 //致,那么就会出现线程异常 try{s.wait();}catch(Exception e){e.printStackTrace();} if(x%2==0){ s.name="lidaofu"; s.age=24; }else{ s.name="李道福"; s.age=23; } x=(x+1)%2; s.flag=true; s.notify(); } } } class Output implements Runnable{ Resource s=null; Output(Resource r){ this.s=r; } public void run(){ while(true) synchronized(s){ //wait(),notify(),notifyAll()这些方法只能在synchronized表示的同步代码块中出现 if(!s.flag) try{s.wait();}catch(Exception e){} System.out.println(s.name+"++++++"+s.age); s.flag=false; s.notify(); } } } public class ProduConsume{ public static void main(String[] args){ Resource r=new Resource(); Input in=new Input(r); Output out=new Output(r); Thread th=new Thread(in); Thread th2=new Thread(out); th.start(); th2.start(); } }
6.锁的升级
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait(),notify(), notifyAll(),替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock(): 上锁
unlock():解锁
newCondition():创建Condition()对象
Condition:替代了Object中的wait(), notify(), notifyAll()
await():等待
signal():唤醒某个线程
signalAll():唤醒所有线程
示例:
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(); } } class Resource { private String name; private int count = 1; private boolean flag = false; //建立锁对象 private Lock lock = new ReentrantLock(); //建立相应的condition对象 private 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,t2 this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; condition_con.signal(); } finally { lock.unlock();//释放锁的动作一定要执行。 } } 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) { } } } }
相关文章推荐
- 黑马程序员————多线程(day22)
- 黑马程序员_Java语言_多线程及GUI
- 黑马程序员——多线程
- 黑马程序员_多线程2
- 黑马程序员_多线程之同步问题的前期,以及安全问题的发现和解决
- 黑马程序员——Java基础---多线程
- 黑马程序员-------------多线程中的(线程、线程组、线程池、以及Java的设计模式)概念及方法的总结
- 黑马程序员-多线程
- 黑马程序员-----多线程
- 黑马程序员_java08_多线程
- 黑马程序员---多线程(二)
- 黑马程序员_Java基础_多线程_12
- 黑马程序员 ---多线程
- 黑马程序员_java基础之多线程
- 黑马程序员-----多线程运行安全(黑马视频)
- 黑马程序员——JAVA笔记之多线程
- 黑马程序员--多线程笔记
- 黑马程序员_java_多线程
- 黑马程序员-----Java基础-----多线程的详解
- 黑马程序员_10多线程