您的位置:首页 > 职场人生

黑马程序员_多线程

2013-05-17 18:38 260 查看
-------
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();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: