黑马程序员-多线程
2013-07-25 22:59
246 查看
---------------------- ASP.Net+Android+IOS开发</a>、.Net培训、期待与您交流! ----------------------
多线程的优势:
1)进程间不能共享内存,但线程之间共享内存非常容易
2)系统创建进程需要为该进程重新分配系统资源,创建线程代价小得多,所以效率高
3)Java语言内置多线程功能支持,简化Java的多线程编程
每启动一个线程都要创建一个Thread类的子类实例,多个线程无法处理同一份资源,多个线程不能共享实例属性
Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run方法
对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是this,也就是该对象本身
静态同步方法的监视器是Class对象
Method:Interface Lock
void lock() //加锁
void unlock() //解锁
Class ReentrantLock:
ReentrantLock类实现了Lock接口,重写了lock、unlock方法
1)对于同步方法,因该类的默认实例(this)就是同步监视器,所以可以在同步方法中直接调用这三个方法
2)对于同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这三个方法
wait() //使当前线程等待,会释放锁
notify() //唤醒在此同步监视器上等待的任意一个线程
notifyAll() //唤醒在此同步监视器上等待的所有线程
当使用Lock对象来保证同步时,Java提供了一个Condition接口来保持协调,如下:
---------------------- 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培训、期待与您交流! ----------------------
相关文章推荐
- 黑马程序员_Java语言_多线程及GUI
- 黑马程序员——多线程
- 黑马程序员_多线程2
- 黑马程序员_多线程之同步问题的前期,以及安全问题的发现和解决
- 黑马程序员——Java基础---多线程
- 黑马程序员-------------多线程中的(线程、线程组、线程池、以及Java的设计模式)概念及方法的总结
- 黑马程序员-多线程
- 黑马程序员-----多线程
- 黑马程序员_java08_多线程
- 黑马程序员---多线程(二)
- 黑马程序员_Java基础_多线程_12
- 黑马程序员 ---多线程
- 黑马程序员_java基础之多线程
- 黑马程序员-----多线程运行安全(黑马视频)
- 黑马程序员——JAVA笔记之多线程
- 黑马程序员--多线程笔记
- 黑马程序员_java_多线程
- 黑马程序员-----Java基础-----多线程的详解
- 黑马程序员_10多线程
- 黑马程序员-多线程(二)