黑马程序员——多线程
------- android培训、java培训、期待与您交流! ----------
1.进程和线程的概念
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元。
线程:是进程中的一个独立的控制单元,在控制着进程的执行。
总之,一个进程中至少有一个线程。
2.自定义线程的方式
(1)继承Thread类,覆盖run方法。
步骤:<1>定义类继承Thread,目的是将自定义的代码存储在run方法中让线程运行。
<2>覆写Thread类中的run方法,该方法有两个作用:启动线程和调用run方法。
代码示例:
//创建线程的第一种方式
//需求:创建两个线程,和主线程交替进行
class Demo extends Thread
{
public Demo(String name)
{
super(name);
}
//覆盖run方法
public void run()
{
for(int i=0;i<50;i++)
{
System.out.println(this.getName()+"在运行......."+i);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//创建线程对象
Demo d1=new Demo("线程一");
Demo d2=new Demo("线程二");
d1.start();
d2.start();
for(int i=0;i<50;i++)
{
System.out.println("主线程在运行........."+i);
}
}
}
(2)实现Runnable接口
步骤:<1>定义类实现Runnable接口;
<2>覆盖Runnable接口中的run方法;
<3>通过Thread类建立线程对象;
<4>将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
<5>调用Thread类的start()方法启动线程并调用Runnable接口子类的run方法。
代码示例:
/*
需求:简单的买票程序,多个窗口同时买票。
*/
class SaleTicket implements Runnable
{
private int ticket=100;
//覆盖run方法
public void run()
{
while(true)
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
System.out.println(Thread.currentThread().getName()+"-----Exception------");
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
}
class SaleTicketDemo2
{
public static void main(String[] args)
{
//创建Runnable子类的实例对象
SaleTicket st=new SaleTicket();
//创造线程对象,将Runnable子类的实例对象作为参数进行传递
Thread t1=new Thread(st);
Thread t2=new Thread(st);
Thread t3=new Thread(st);
//启动线程
t1.start();
t2.start();
t3.start();
}
}
以上两种方式,实现接口的方式避免了单继承的局限性,所以在定义线程时,建议使用实现接口的方式。
3.线程的运行状态:
如下图所示:
4.多线程安全问题
出现此问题的原因:当多条语句在操作同一个线程中的共享数据时,一个线程多条语句只执行了一部分,还没有执行完时,另一个线程参与进来执行。从而导致了共享数据的错误。
解决方法有两种:同步代码块或者同步方法。
(1)同步代码块:synchronized(对象){需要被同步的代码;}
代码示例:
/*
需求:简单的买票程序,多个窗口同时买票。
*/
class SaleTicket implements Runnable
{
private int ticket=100;
Object obj=new Object();
public void run()
{
while(true)
{
//同步代码块
synchronized(obj)
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
}
}
class SaleTicketDemo3
{
public static void main(String[] args)
{
SaleTicket st=new SaleTicket();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
Thread t3=new Thread(st);
t1.start();
t2.start();
t3.start();
}
}
(2)同步方法:只需要将方法进行同步就可以。
代码示例:
/*
同步代码块与同步函数互相切换。
*/
class SaleTicket implements Runnable
{
private int ticket=100;
//定义标记用于切换
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
//同步代码块
synchronized(this)
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
}
else
while(true)
method();
}
//同步方法
public synchronized void method()
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
class SaleTicketDemo4
{
public static void main(String[] args)
{
SaleTicket st=new SaleTicket();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
try{
Thread.currentThread().sleep(10);
}
catch(Exception e)
{
System.out.println(Thread.currentThread().getName()+"-----Exception------");
}
st.flag=false;
t2.start();
}
注意:同步的前提条件:
<1>必须要有两个或者两个以上的线程;
<2>必须是多个线程使用同一个锁;
<3>必须保证同步中只能有一个线程在运行;
同步的好处:解决了多线程的安全问题
同步的弊端:多个线程需要判断对象锁,较为浪费资源。
静态同步方法使用的对象锁是该方法所在类的字节码文件对象,即类名.class.
代码实例:
class SaleTicket implements Runnable
{
private static int ticket=100;
//定义标记用于切换
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
//同步代码块
synchronized(SaleTicket.class)
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
}
else
while(true)
method();
}
//静态同步方法
public static synchronized void method()
{
if(ticket>0){
try{
Thread.sleep(10);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"卖了第有"+ticket--+"张票");
}
}
}
class SaleTicketDemo5
{
public static void main(String[] args)
{
SaleTicket st=new SaleTicket();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
try{
Thread.currentThread().sleep(10);
}
catch(Exception e)
{
}
st.flag=false;
t2.start();
}
}
5.线程间的通信
线程间的通信其实就是多个线程在操作同一个资源,但是操作的动作不同。
代码示例:
//线程之间的通信,通过等待唤醒机制来实现
class Information
{
private String name;
private String sex;
boolean flag=false;
//同步方法
public synchronized void set(String name,String sex)
{
//判断标记,如果为真表示已经赋值,应该等待被打印
if(this.flag)
{
try{
this.wait();
}
catch(Exception e){}
}
//赋值
this.name=name;
this.sex=sex;
//改变标记,表示赋值成功
this.flag=true;
//唤醒等待的线程
this.notify();
}
public synchronized void get()
{
//判断标记,如果为假,表示未被赋值,应该等待被赋值
if(!this.flag)
{
try
{
this.wait();
}
catch(Exception e)
{
System.out.println(Thread.currentThread().getName()+".......Exception....");
}
}
System.out.println(name+"----"+sex);
//改变标记,表示打印完毕
this.flag=false;
//唤醒等待的线程
this.notify();
}
}
//赋值线程类
class Input implements Runnable
{
private Information infor;
public Input(Information infor)
{
this.infor=infor;
}
public void run()
{
boolean flag=true;
while(true)
{
if(flag)
{
//调用方法,完成赋值操作
infor.set("Apple","girl");
flag=false;
}
else
{
infor.set("小明","男孩");
flag=true;
}
}
}
}
//输出线程类
class Output implements Runnable
{
private Information infor;
public Output(Information infor)
{
this.infor=infor;
}
public void run()
{
while(true)
{
//调用方法完成输出操作
infor.get();
}
}
}
class ThreadComDemo
{
public static void main(String[] args)
{
Information infor=new Information();
new Thread(new Input(infor)).start();
new Thread(new Output(infor)).start();
}
}
6.生产者与消费者模型
使用同步方法的形式来实现:
代码示例:
//多个生产者与消费者
class ProCusDemo
{
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(pro);
Thread t11=new Thread(con);
Thread t22=new Thread(con);
Thread t33=new Thread(con);
t1.start();
t2.start();
t3.start();
t11.start();
t22.start();
t33.start();
}
}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{
//循环判断标记
while(flag)
{
try
{
wait();
}
catch (Exception e)
{
System.out.println("线程被终止!!!!!!");
}
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
flag=true;
//唤醒所有
this.notifyAll();
}
public synchronized void out()
{
//循环判断
while(!flag)
{
try
{
wait();
}
catch (Exception e)
{
System.out.println("线程被终止!!!!!!");
}
}
System.out.println(Thread.currentThread().getName()+"---消费者----"+this.name);
flag=false;
//唤醒所有
this.notifyAll();
}
}
//生产者
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
JDK1.5中提供了多线程升级的解决方案:
代码示例:
import java.util.concurrent.locks.*;
//多个生产者与消费者
class ProCusDemo2
{
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(pro);
Thread t11=new Thread(con);
Thread t22=new Thread(con);
Thread t33=new Thread(con);
t1.start();
t2.start();
t3.start();
t11.start();
t22.start();
t33.start();
}
}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition pro_condition=lock.newCondition();
private Condition con_condition=lock.newCondition();
public void set(String name)
{
lock.lock();
try
{
//循环判断标记
while(flag)
//生产者线程等待
pro_condition.await();
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
flag=true;
//唤醒消费者线程
con_condition.signalAll();
}
catch(InterruptedException e)
{
System.out.println("线程被终止!!!!!!");
}
finally
{
//释放锁
lock.unlock();
}
}
public void out()
{
lock.lock();
try
{
//循环判断
while(!flag)
//消费者线程等待
con_condition.await();
System.out.println(Thread.currentThread().getName()+"---消费者----"+this.name);
flag=false;
//唤醒生产者线程
pro_condition.signalAll();
}
catch (InterruptedException e)
{
System.out.println("线程被终止!!!!!!");
}
finally
{
lock.unlock();
}
}
}
//生产者
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
升级之后的解决方案,显示的对象所机制、显示的等待唤醒机制,一个锁可以对应多个Condition对象,可以实现在唤醒对方锁的同时不用唤醒己方锁,较之前的解决方案更好一些。
7.停止线程。
解决思路:结束run方法。当开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。
代码示例:
class Test implements Runnable
{
private boolean flag=true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"...run");
}
}
public void changeflag()
{
flag=false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
Test test=new Test();
Thread t1=new Thread(test);
Thread t2=new Thread(test);
t1.start();
t2.start();
int number=0;
while(true)
{
if(number==50)
{
test.changeflag();
break;
}
number++;
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
特殊情况:当线程处于冻结状态,就不会读取到标记,此时可以通过调用interrupt()方法将处于冻结状态的线程强制恢复到运行状态。
代码示例:
class Test implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+"...Excption");
flag=false;
}
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
class StopThreadDemo2
{
public static void main(String[] args)
{
Test test=new Test();
Thread t1=new Thread(test);
Thread t2=new Thread(test);
t1.start();
t2.start();
int number=0;
while(true)
{
if(number==50)
{
//清除线程的冻结状态
t1.interrupt();
t2.interrupt();
break;
}
number++;
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
8.线程池
使用线程池来执行线程任务的步骤如下:
(1)调用Executors类的静态方法创建一个ExecutorsService对象,该对象就代表一个线程池。
(2)创建Runnable实现类的实例,作为线程执行任务。
(3)调用ExecutorsService对象的submit()方法来提交Runnable示例。
(4)当不想提交任何任务时,调用ExecutorsService对象的shutdown()方法来关闭线程池。
代码示例:
import java.util.concurrent.*;
class ThreadPoolDemo
{
public static void main(String[] args) throws Exception
{
//创建一个具有固定线程数的线程池
ExecutorService pool=Executors.newFixedThreadPool(5);
//向线程池中提交两个线程
pool.submit(new Demo());
pool.submit(new Demo());
//关闭线程池
pool.shutdown();
}
}
//线程任务类
class Demo implements Runnable
{
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+"----"+i+"-------");
}
}
}
------- android培训、java培训、期待与您交流! ----------