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

黑马程序员-多线程

2013-07-25 22:59 246 查看
---------------------- ASP.Net+Android+IOS开发</a>、.Net培训、期待与您交流! ----------------------

多线程

当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。比如main方法会启动一个线程,后台的垃圾回收线程等

多线程的优势:

1)进程间不能共享内存,但线程之间共享内存非常容易

2)系统创建进程需要为该进程重新分配系统资源,创建线程代价小得多,所以效率高

3)Java语言内置多线程功能支持,简化Java的多线程编程

继承Thread类创建线程

定义Thread类的子类,并重写run方法,创建Thread子类的实例,用线程对象的start方法启动线程

每启动一个线程都要创建一个Thread类的子类实例,多个线程无法处理同一份资源,多个线程不能共享实例属性

/**
其中的两个线程分别处理各自类中的资源
*/

class FirstThread extends Thread
{
public static void main(String[] args)
{
new FirstThread().start();
new FirstThread().start();

for(int i = 0; i < 100; i++)
{
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}

public void run()
{
for(int i = 0; i < 100; i++)
{
//Thread对象的getName()返回当前该线程的名字
System.out.println(getName()+"---"+i);
}
}
}


实现Runnable接口创建线程

定义Runnable接口的实现类,重写run方法。创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,调用线程对象的start方法来启动该线程

Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run方法

/**
向两个线程传入同一个target,会共享target的成员变量

同时,带来了线程安全问题:两个线程处理同一份资源ticket,存在这种可能:当ticket为1时,第一个线程进入for循环在①处停住了,第二个线程又进入了for循环并成功卖出了最后一张票,ticket变成0,但第一个线程还在for循环内部,又卖出了不存在的0,就出现了问题。

可以通过同步代码块、同步方法、同步锁解决这个问题
*/

class Ticket implements Runnable
{
private int ticket = 100;

public void run()
{
for(; ticket > 0; ticket--)
{
//①
System.out.println(Thread.currentThread().getName() + "+++++" + ticket);
}
}
}

class ThreadTest2
{
public static void main(String[] args)
{
Ticket t = new Ticket();

//传入同一个对象t,两个线程共享t的成员变量
new Thread(t).start();
new Thread(t).start();
}
}


同步代码块

同步代码块可以解决多线程中的安全问题,同步代码块需要同步监视器,同步监视器可以是任意对象

synchronized(obj)
{
...
//同步代码块
}
obj就是同步监视器,线程开始执行同步代码块之前,必须先获得对同步监视器的锁定,任何时刻只能有一条线程可以获得对同步监视器的锁定

同步方法

同步方法就是使用synchronized关键字来修饰某个方法,该方法称为同步方法

对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是this,也就是该对象本身

静态同步方法的监视器是Class对象

/**
同步方法
*/

class SynchronizedMethodTest
{
public static void main(String[] args)
{
Ticket t = new Ticket();

new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}

class Ticket implements Runnable
{
private int tickets = 1000;

public void run()
{
while(true)
{
sellTicket();
}
}

//同步方法持有的监视器是this
public synchronized void sellTicket()
{
if(tickets > 0)
{
System.out.println(Thread.currentThread().getName() + "+++" + (tickets--));
}
else
{
System.exit(1);
}
}
}


同步锁

从1.5之后,Java提供了另外一种线程同步的机制:它通过显示定义同步锁对象来实现同步

Method:Interface Lock

void lock()    //加锁

void unlock()    //解锁

Class ReentrantLock:

ReentrantLock类实现了Lock接口,重写了lock、unlock方法

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
/**
同步锁
*/

class Ticket implements Runnable
{
private int tickets = 1000;
//定义ReentrantLock
Lock lock = new ReentrantLock();

public void run()
{
while(true)
{
//加锁
lock.lock();
try
{
if(tickets > 0)
{
System.out.println(Thread.currentThread().getName()+"+++"+tickets--);
}
else
{
System.exit(1);
}
}
//解锁,在finally块中可以保证执行
finally
{
lock.unlock();
}
}
}
}

class ThreadTest2
{
public static void main(String[] args)
{
Ticket t = new Ticket();

new Thread(t).start();
new Thread(t).start();
}
}


死锁

当两个线程相互等待对方释放同步监视器时就会发生死锁,两个线程各持一把锁,想要对方的锁,但各自必须要到锁才可以放锁,就死锁了

/**
死锁
*/

//创建两个监视器
class MyLock
{
static Object lock1 = new Object();
static Object lock2 = new Object();
}

class Thread1 implements Runnable
{
public void run()
{
while(true)
{
synchronized(MyLock.lock1)
{
System.out.println("Thread1...lock1");
synchronized(MyLock.lock2)
{
System.out.println("Thread1...lock2");
}
}
}
}
}

class Thread2 implements Runnable
{
public void run()
{
while(true)
{
synchronized(MyLock.lock2)
{
System.out.println("Thread2...lock2");
synchronized(MyLock.lock1)
{
System.out.println("Thread2...lock1");
}
}
}
}
}

class DeadlockTest
{
public static void main(String[] args)
{
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
}


线程的协调运行

根类Object提供的wait()、notify()、notifyAll()三个方法,必须由同步监视器对象来调用,分两种情况:

1)对于同步方法,因该类的默认实例(this)就是同步监视器,所以可以在同步方法中直接调用这三个方法

2)对于同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这三个方法

wait()    //使当前线程等待,会释放锁

notify()    //唤醒在此同步监视器上等待的任意一个线程

notifyAll()    //唤醒在此同步监视器上等待的所有线程

当使用Lock对象来保证同步时,Java提供了一个Condition接口来保持协调,如下:

import java.util.concurrent.locks.*;
/**
生产者消费者例子

多个生产者生产产品,多个消费者购买产品。实现逻辑是生产一个产品,消费一个产品,使用Condition控制线程的协调运行

本例中使用的Lock和Condition,如果使用同步方法,则把await改为wait(),signal改为notifyAll
*/

class ProducerAndConsumerTest
{
public static void main(String[] args)
{
//启动两个生产者线程
new Thread(n
ad68
ew Producer()).start();
new Thread(new Producer()).start();

//启动两个消费者线程
new Thread(new Consumer()).start();
new Thread(new Consumer()).start();
}
}

class Producer implements Runnable
{
public void run()
{
while(true)
{
Product.in();
}
}
}

class Consumer implements Runnable
{
public void run()
{
while(true)
{
Product.out();
}
}
}

class Product
{
private static int num = 0;    //产品编号

private static boolean flag = false;    //是否有产品

private static Lock lock = new ReentrantLock();

private static Condition con_in = lock.newCondition();
private static Condition con_out = lock.newCondition();

public static void in()
{
lock.lock();
try
{
//使用while而不是if的原因:当等待线程被唤醒时,仍需要通过flag判断是否存在商品
while(flag)
{
try{con_in.await();}catch(Exception e){}    //线程等待并释放锁
}

//打印一行语句,说明已生产商品
System.out.println(Thread.currentThread().getName() + "+++" + (++num));

//设置标记为true,说明存在商品
flag = true;
//唤醒消费者的一个线程(随机)
con_out.signal();
}
finally
{
lock.unlock();
}
}

public static void out()
{
lock.lock();
try
{
while(!flag)
{
try{con_out.await();}catch(Exception e){}
}
System.out.println(Thread.currentThread().getName() + "---" + num);
flag = false;
con_in.signal();
}
finally
{
lock.unlock();
}
}
}


---------------------- ASP.Net+Android+IOS开发</a>、.Net培训、期待与您交流! ----------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: