黑马程序员_多线程
来源:互联网 发布:数据库中删除重复数据 编辑:程序博客网 时间:2024/06/05 16:41
------- android培训、java培训、期待与您交流! ----------
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。
一个进程中至少有一个线程。
Java VM启动的时候会有一个进程java.exe,该进程中至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还负责垃圾回收机制的线程。
如何在自定义的代码中自定义一个线程?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类。
创建线程的第一种方式:继承Thread类。
步骤:
1、定义类继承Thread;
2、复写Thread类中的run方法;
目的:将自定义的代码存储在run方法中,让线程运行。
3、调用线程的start方法,该方法两个作用:启动线程,调用run方法。
为什么覆盖run方法呢?
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
class Demo extends Thread{ publicvoid run() { System.out.println("demorun"); }}class ThreadDemo{ publicstatic void main(String[] args) { Demod=new Demo();//创建好一个线程。 d.start(); }}
线程都有自己默认的名称:Thread-编号,该编号从0开始。
static Thread currentThread():获取当前线程对象。
getName():获取线程名称。
class Test extends Thread{ Test(Stringname) { super(name); } publicvoid run() { System.out.println(Thread.currentThread().getName()); }}class ThreadTest{ publicstatic void main(String[] args) { Testt1=new Test("one"); Testt2=new Test("two"); t1.start(); t2.start(); }}
创建线程的第二种方式,实现Runable接口。
步骤:
1、定义类实现Runnable接口;
2、覆盖Runnable接口中的run方法;
将线程要运行的代码存放在该run方法中。
3、通过Thread类建立线程对象;
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
为什么要将Runnable接口的子类对象传递给Thread的构造函数?因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去运行指定对象的方法,就必须明确该run方法所属的对象。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法中。
多线程运行出现安全问题:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
同步的前提:
1、必须要有两个或者两个以上的线程;
2、必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题;
弊端:多个线程都需要判断锁,较为消耗资源。
class Ticket implements Runnable{ privateint tick=100; Objectobj=new Object(); publicvoid run() { while(true) { synchronnized(obj) { try{Thread.sleep(10);}catch(Exceptione){} if(tick>0) { System.out.println(Thread.currentThread().getName()+"==="+tick--); } } } }}class TicketDemo{ publicstatic void main(String[] args) { Tickett=new Ticket(); Threadt1=new Thread(t);//创建线程 Threadt2=new Thread(t); Threadt3=new Thread(t); Threadt4=new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); }}
需求:
银行有一个金库,有两个储户分别存300元,每次存100,存3次。
目的:该程序是否有安全问题,如果有如何解决。
如何找问题:
1、明确哪些代码是多线程运行代码;
2、明确共享数据;
3、明确多线程运行代码中哪些语句是操作共享数据的。
class Bank{ privateint sum; publicsynchronized void add(int n)//同步函数,和同步块功能一样 { sum=sum+n; System.out.println("sum="+sum); }}class Cus implements Runnable{ privateBank b=new Bank(); publicvoid run() { for(intx=0;x<3;x++) { b.add(100); } }}class BankDemo{ publicstatic void main(String[] args) { Cusc=new Cus(); Threadt1=new Thread(c); Threadt2=new Thread(c); t1.start(); t2.start(); }}
同步函数的锁是this。
如果同步函数被静态修饰后,使用的锁不再是this,因为静态方法中不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。该对象的类型是Class,类名.class。
静态同步方法,使用的锁是该方法所在类的字节码对象,类名.class。
单例设计模式:
饿汉式
class Single{ privatestatic final Single s=new Single(); privateSingle(){} publicstatic Single getInstance() { returns; }}
懒汉式//实例延迟加载,如果多线程访问时会出现安全问题,用加同步解决。
class Single{ privatestatic Single s=null; privateSingle(){} publicstatic Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) s=newSingle(); } } returns; }}class SingleDemo{ publicstatic void main(String[] args) { System.out.println("HelloWorld!"); }}
死锁:同步中嵌套同步
class Test implements Runnable{ privateboolean flag; Test(booleanflag) { this.flag=flag; } publicvoid run() { if(flag) { synchronized(MyLock.locka) { System.out.print("iflocka"); synchronized() {System.out.print("iflockb");} } } else { synchronized(MyLock.lockb) { System.out.print("elselockb"); synchronized(MyLock.locka) {System.out.print("elselocka");} } } }}class MyLock{ staticObject locka=new Object(); staticObject lockb=new Object();}class DeadLockTest{ publicstatic void main(String[] args) { Threadt1=new Thread(new Test(true)); Threadt2=new Thread(new Test(false)); t1.start(); t2.start(); }}
线程间通信
思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
1、这些方法存在于同步中;
2、使用这些方法时必须要标识所属的同步锁;
3、锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
思考2:wait(),sleep()有什么区别?
wait():释放资源,释放锁。
sleep():释放资源,不释放锁.
线程间通讯:其实就是多个线程在操作一个资源,但是操作的动作不同。
等待唤醒机制:
notifyAll():
class Res{ privateString name; privateString sex; privateboolean flag=false; publicsynchronized void set(String name,String sex) { if(flag) try{this.wait();}catch(Exceptione){} this.name=name; this.sex=sex; flag=true; this.notify(); } publicsynchronized void out() { if(!flag) try{this.wait();}catch(Exceptione){} System.out.println(name+""+sex); flag=false; this.notify(); }}class Input implements Runnable{ privateRes r; Input(Resr) { this.r=r; } publicvoid run() { intx=0; while(true) { if(x==0) r.set("mike","man"); else r.set("丽丽","女"); x=(x+1)%2; } }}class Output implements Runnable{ privateRes r; Output(Resr) { this.r=r; } publicvoid run() { while(true) { r.out(); } }}class InputOutputDemo{ publicstatic void main(String[] args) { Resr=new Res(); newThread(new Input(r)).start(); newThread(new Output(r)).start(); //Inputin=new Input(r); //OutputOut=new Output(r); //Threadt1=new Thread(in); //Threadt2=new Thread(out); //t1.start(); //t2.start(); }}
生产者消费者
class ProducerConsumerDemo{ publicstatic void main(String[] args) { Resourcef=new Resource(); Producerpro=new Producer(r); Consumercon=new Consumer(r); Threadt1=new Thread(pro); Threadt2=new Thread(con); t1.start(); t2.start(); }}class Resource{ privateString name; privateint count=1; privateboolean flag=false; publicsynchronized void set(String name) { while(flag) try{wait();}catch(Exceptione){} this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"生产者"+this.name); flag=true; this.notifyAll(); } publicsynchronized void out() { while(!flag) try{wait();}catch(Exceptione){} System.out.println(Thread.currentThread().getName()+"消费者"+this.name); flag=false; this.notifyAll(); }}class Producer implements Runnable{ privateResource res; producer(Resourceres) { this.res=res; } publicvoid run() { while(true) { res.set("+商品+"); } }}class Consumer implements Runnable{ privateResource res; producer(Resourceres) { this.res=res; } publicvoid run() { while(true) { res.out("+商品+"); } }}
对于多个生产者和消费者,为什么要定义while判断标记?
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll?
因为需要唤醒对方线程。只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
接口Condition:
Condition将Object监视器方法(wait、notify和nofifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。
import java.util.concurrent.locks.*;class ProducerConsumerDemo{ publicstatic void main(String[] args) { Resourcef=new Resource(); Producerpro=new Producer(r); Consumercon=new Consumer(r); Threadt1=new Thread(pro); Threadt2=new Thread(con); t1.start(); t2.start(); }}class Resource{ privateString name; privateint count=1; privateboolean flag=false; privateLock lock=new ReentrantLock(); privateCondition condition_pro=lock.newCondition(); privateCondition condition_con=lock.newCondition(); publicvoid set(String name)throws InterruptedException { lock.lock(); try { while(flag) condition_pro.await(); this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"生产者"+this.name); flag=true; condition_con.signal(); } finally { lock.unlock(); } } publicsynchronized 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{ privateResource res; producer(Resourceres) { this.res=res; } publicvoid run() { while(true) { try { res.set("+商品+"); } catch(InterruptedExceptione) { } } }}class Consumer implements Runnable{ privateResource res; producer(Resourceres) { this.res=res; } publicvoid run() { try { res.out("+商品+"); } catch(InterruptedExceptione) { } }}
JDK1.5中提供了多线程升级解决方案。
将同步synchronized替换成显示的Lock操作。
将Objec中的wait,notify,notifyAll替换了Condition对象。
该对象可以通过Lock锁进行获取。
在该示例中,实现了本方只唤醒对方的操作。
显示的锁机制以及显示的锁对象上的等待唤醒操作机制,一个锁对应多个condition。
stop方法已经过时。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。
清除线程的冻结状态:Thread类中提供了该方法:interrupt()。
当没有指定的方式让线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
public final void setDaemon(booleanb on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。
该方法必须在启动线程前调用。
class stopThread implements Runnable{ privateboolean flag=true; publicvoid run() { while(flag) { try { wait(); } catch(InterruptedExceptione) { System.out.println(Thread.currentThread().getName()+"...Exception"); flag=false; } System.out.println(Thread.currentThread().getName()+"...run"); } } publicvoid changeFlag() { flag=false; }}class StopThreadDemo{ publicstatic void main(String[] args) { StopThreadst=new StopThread(); Threadt1=new Thread(st); Threadt2=new Thread(st); //t1.setDaemon(true);守护线程 //t2.setDaemon(true);守护线程 t1.start(); t2.start(); intnum=0; while(true) { if(num++== 60) { //st.changeFlag(); t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"..."+num); } }}
join:
当a线程执行到了b线程的join()方法时,a线程就会等待,等b线程都执行完,a才会执行。join可以用来临时加入线程执行。
class Demo implements Runnable{ publicvoid run() { for(intx=0;x<70;x++) { System.out.println(Thread.currentThread().getName()+"..."+x); } }}class JoinDemo{ publicstatic void main(String[] args)throws Exception { Demod=new Demo(); Threadt1=new Thread(d); Threadt2=new Thread(d); t1.start(); t1.setPriority(Thread.MAX_PRIORITY);//提高优先级,相当于10 t1.join(); t2.start(); for(intx=0;x<80;x++) { System.out.println("main..."+x); } System.out.println("over"); }}
yield()暂停当前正在执行的线程对象,并执行其他线程。
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- Ubuntu12 下安装Tomcat7
- 通过源码学算法--AdaBoost: CART插播
- FL2440移植linux-3.0全过程(2) make menuconfig
- Coding with Reason
- 十大畅销【操作系统类】书籍,说说你看过哪本
- 黑马程序员_多线程
- poj——校门外的树解题报告
- Ubuntu12下安装MongoDB
- A Comment on Comments
- 常用的软件
- Codeforces Beta Round #43 (ACM-ICPC Rules), problem: (D) Parking Lot 线段树 区间合并
- Win7下硬盘安装 Red Hat Enterprise Linux 6.3图文方法
- Comment Only What the Code Cannot Say
- Android仿人人客户端(v5.7.1)——网络模块时序图