多线程
当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。比如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(new 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培训、期待与您交流! ----------------------