Java 线程的基本概念(模仿生产者与消费者的问题)
2012-07-22 14:10
357 查看
1.什么是线程?
程序中一个单一顺序的控制流程。
2.线程与进程的区别?
每个进程都有独立的代码和数据空间,进程间的切换有较大的开销。
线程可以看做是轻量级的进程,是进程的一个实例。同一类线程共享代码和数据空间,每个线程都有独立的运行栈和程序计数器(PC),线程间切换开销小。
3.多线程与多进程
多进程:在操作系统中能同时运行多个任务(程序)
多线程:同一应用程序中有多个顺序流在同时执行。
4.线程创建与启动的方法有两种
实现Runable接口
继承Thread类
5.线程的转换状态
创建 、就绪、运行、阻塞和终止状态。
6.线程控制的基本方法
isAlive:判断线程是否活着,就绪、运行和阻塞都是活着。
getPriority(),setPriority():获取或设置线程的优先级
Thread.sleep():当前线程sleep的毫秒数
join():线程合并
yield():让出CUP,当前线程进入就绪队列,等待CPU调度
wait():当前线程进入等待池(wait pool)
notify()/notifyAll():唤醒 wait pool 中的线程或全部线程
(1)Sleep():可以用Thread.sleep()直接调用。会抛出InterruptedExcepteion
(2) Join() :合并线程,合并后会抛出InterruptedException异常
7.线程同步:协调多个线程访问同一个资源
运行结果:t1,你是第1个使用Timer的线程
t2,你是第2个使用Timer的线程
如果不加Synchronizaed等关键字,运行的结果是:t1,你是第2个使用Timer的线程
t2,你是第2个使用Timer的线程
原因:首先,t1访问timer.add,num++,num=1;
接着,t1线程sleep(1),即使没有sleep(1)结果也会如此,因为CPU的时间片段会打断线程的运行。
然后,t2访问timer.add,num++,num=2;
接着,t2.sleep(1),t1唤醒了继续执行,由于num是static变量,因此输出的num=2;
最后,t2醒后,输出num=2.
最主要的原因是,线程在sleep时没有将当前线程正在使用的资源进行 锁住,让其他的线程进来破坏了共享数据的完整性,因此加入关键字Synchronized,它来与对象间的互斥锁联系,当个资源被synchronized修饰时,表示该资源在任意时刻只能有一个线程访问。
8.java模拟一个死锁的 例子
9.写一个线程同步的例子
接着,主线程Main也Thread.sleep(1000)后,执行tt.m2(),而 m2中Thread.sleep(7500),而此时t线程已经唤醒,因此先输出b=1000;
最后,主线程被唤醒,b的值改为2000,最后输出2000。
注意:如果某个资源会使线程产生死锁,那么必须正确的为该资源上锁,必须把访问到这个资源的所有方法都考虑到。加了同步效率可能会降低,当不加 同步可能产生数据不一致的问题。
10 生产者与消费者问题
生产者 往容器类生产产品 ,直到到达容器的上限必须停止生产,等待消费者的消费。
消费者消费容器中的产品,直到容器中的产品消费完了,等待生产者生产产品。
代码实现:
(1)在一个线程中任何地方都可以条用sleep()方法,当只有加锁的对象才能调用wait(),也就是说只有用Synchronized的地方才能用wait();
(2)wait()是object类的方法,而sleep()是Thread类的方法;
(3)加锁的对象调用了wait()后,这个线程就不在拥有锁了,只有被 notify()叫醒后,才能找回该锁,而加锁的对象调用了sleep()后,这个线程依然用后该锁;
(4)wait()必须用notify()才能被叫醒,而sleep(1000),当过1000毫秒后就自动唤醒。
11.关于线程的一些基本的问题
java中有几种方法可以实现创建一个线程?
有两种方法创建一个线程:第一种方法是定义线程实现Runnable接口,调用的是Runnable接口的run()方法,即要重写Runnable接口的run()方法(推荐使用)
第二个方法是定义线程继承Thread方法,调用Thread父类的run()方法。
stop()和suspend()方法为什么不推荐使用?
(1)反对用Stop()方法,因为它不安全,它会解除由线程获取的所有锁,而且如果对象处于一种不连贯的状态,那么其他线程能在那种状态下检查和修改它们。
(2)反对用Suspend()方法,因为此方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然保持在此之间获得锁。因此其他任何线程都不能 访问锁定的资源,除非被suspend()挂起的线程状态恢复。
对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。
因此不应该使用suspend(),若线程要挂起就使用wait()方法,命其进入等待状态,因为加锁的对象调用了wait()后,此线程就不在拥有锁了,只有被notify()唤醒,才能找回该锁。
sleep()和wait()方法的区别?
线程同步和异步有何异同,分别在什么情况下使用它们?
(1)如果数据将在线程间共享,例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程谢过了 ,那么这些数据是共享数据,必须进行
同步读取。
(2)当应用程序在对象上调用了一个需要很长时间执行的方法,并不希望让程序等待该方法的返回值,这时应该异步编程,异步的效率更高。
当一个线程进入一个对象的Synchronized方法后,其他线程是否可以进入此对象的其他方法?
要分几种情况来讨论:
(1)如果此对象的其他方法前没有加Synchronized,就可以访问这些方法。
(2)如果此对象的其他方法前加了Synchronized,但是当前Synchronized方法内部 有wait(),则这个线程可以进入其它加了Synchronized的方法。
(3)如果当前Synchronized方法内部没有wait(),则 这个线程不能进入加入其它的Synchronized方法内部。
线程有哪些基本的状态,这些状态之间的关系是什么?
一个程序可以有多个线程同时执行,每个线程都有关联要执行的代码,而每个程序至少 有一个线程,即Main主线程。
当一个线程start()后,该线程就进入了就绪状态,等待CPU的调度,调度后就绪状态的线程就变成运行状态。
在运行 状态线程遇到某个对象的Synchronized语句时,但又获得不了LOCK时候,就由运行状态变成阻塞状态。
当Synchronized获得锁LOCK后,线程再由阻塞状态变成运行状态,同时可以使用wait ()转成挂起状态。
当线程相关代码执行完成后,线程变为结束状态。
程序中一个单一顺序的控制流程。
2.线程与进程的区别?
每个进程都有独立的代码和数据空间,进程间的切换有较大的开销。
线程可以看做是轻量级的进程,是进程的一个实例。同一类线程共享代码和数据空间,每个线程都有独立的运行栈和程序计数器(PC),线程间切换开销小。
3.多线程与多进程
多进程:在操作系统中能同时运行多个任务(程序)
多线程:同一应用程序中有多个顺序流在同时执行。
4.线程创建与启动的方法有两种
实现Runable接口
class Runner1 implements Runables{ public void run() { for (int i=0;i<100;i++) { System.out.println("Runner1="+i); } } } public class TestThread1 { public static void main(String args[]) { Runner1 r = new Runner1(); Thread t = new Thread(r); t.start(); for(int i=0;i<100;i++) { System.out.println("Main Thread:--"+i); } } }
继承Thread类
class Runner extends Thread { public void run() { for(int i=0;i<100;i++) { System.out.println("Runner1="+i); } } } public class Test{ public static void main(String args[]) { Runner r = new Runner(); r.start(); } }
5.线程的转换状态
创建 、就绪、运行、阻塞和终止状态。
6.线程控制的基本方法
isAlive:判断线程是否活着,就绪、运行和阻塞都是活着。
getPriority(),setPriority():获取或设置线程的优先级
Thread.sleep():当前线程sleep的毫秒数
join():线程合并
yield():让出CUP,当前线程进入就绪队列,等待CPU调度
wait():当前线程进入等待池(wait pool)
notify()/notifyAll():唤醒 wait pool 中的线程或全部线程
(1)Sleep():可以用Thread.sleep()直接调用。会抛出InterruptedExcepteion
import java.util.*; class MyThread extends Thread { public void run() { while(true) { System.out.println("==="+new Date()+"==="); try { sleep(1000); } catch(InterruptedException e) { return; } } } } public class TestInterrupt { public static void main(String args[]) { MyThread thread = new MyThread(); thread.Start(); try { Thread.Sleep(10000); } catch(InterruptedException e) { } thread.interrupt(); } }
(2) Join() :合并线程,合并后会抛出InterruptedException异常
import java.util.*; class MyThread2 extends Thread { Mythread2(String s) { super(s); } public void run() { for(int i=0;i<10;i++) { System.out.println("I AM"+getName()); } try { sleep(1000); } catch(InterruptedException e) { return; } } } public class TestInterrupt { public static void main(String args[]) { MyThread2 t = new MyThread2("t"); t.start(); try { t.join(); } catch(InterruptedException e) { } for(int i=0;i<10;i++) { System.out.println("I Am Main Thread"); } } }
7.线程同步:协调多个线程访问同一个资源
class Timer{ private static int num=0; public Synchronized void add(String name) { Synchronized(this) { num++; try { Thread.sleep(1); }catch(interruptedException e){} System.out.println(name+",你是第"+num+"使用Timer的线程"); } } } public class TestSync implements Runnable{ Timer timer = new Timer(); public Static void Mian(String args[]) { TestSync test = new TestSync(); Thread t1= new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } public void run() { timer.add(Thread.currentThread().getName()); } }注意:
运行结果:t1,你是第1个使用Timer的线程
t2,你是第2个使用Timer的线程
如果不加Synchronizaed等关键字,运行的结果是:t1,你是第2个使用Timer的线程
t2,你是第2个使用Timer的线程
原因:首先,t1访问timer.add,num++,num=1;
接着,t1线程sleep(1),即使没有sleep(1)结果也会如此,因为CPU的时间片段会打断线程的运行。
然后,t2访问timer.add,num++,num=2;
接着,t2.sleep(1),t1唤醒了继续执行,由于num是static变量,因此输出的num=2;
最后,t2醒后,输出num=2.
最主要的原因是,线程在sleep时没有将当前线程正在使用的资源进行 锁住,让其他的线程进来破坏了共享数据的完整性,因此加入关键字Synchronized,它来与对象间的互斥锁联系,当个资源被synchronized修饰时,表示该资源在任意时刻只能有一个线程访问。
8.java模拟一个死锁的 例子
class TestDeadLock implements Runnable { public int flag=1; Static objec o1 = new object(); Static objec o2 = new object(); public void run() { System.out.println("flag="+flag); if(flag==1) { Synchronized(o1) { try { Thread.sleep(500); }catch(InterruptedException e){} Synchronized(o2) { System.out.println("1"); } } } if(flag==0) { Synchronized(02) { try { Thread.sleep(500); }catch(InterruptedExcetpion e){} Synchronized(01) { System.out.prinltn("0"); } } } } public Static void Main(String args[]) { TestDeadLock td1 = new TestDeadLock(); TestDeadLock td2 = new TestDeadLock(); td1.flag=1; td2.flag=0; Thread t1 = new Thread(td1); Thread t2 = new Thread(td2); t1.start(); t2.start(); } }
9.写一个线程同步的例子
public class TT implements Runable { int b=100; public Synchronized void m1() throws Exception { b=1000; Thread.sleep(5000); System.out.println("b="+b); } public void m2() throws Exception { Thread.sleep(7500); b=2000; System.out.println(b); } public void run() { try { m1(); } catch(Exception e) { e.PrintstackTrace(); } } public Static void Main(String args[]) { TT tt = new TT(); Thread t = new Thread(tt); t.start(); Thread.sleep(1000); tt.m2(); } }分析:首先,t线程开始执行,进入m1()方法,将b的值改为b=1000,t线程开始sleep(5000);
接着,主线程Main也Thread.sleep(1000)后,执行tt.m2(),而 m2中Thread.sleep(7500),而此时t线程已经唤醒,因此先输出b=1000;
最后,主线程被唤醒,b的值改为2000,最后输出2000。
注意:如果某个资源会使线程产生死锁,那么必须正确的为该资源上锁,必须把访问到这个资源的所有方法都考虑到。加了同步效率可能会降低,当不加 同步可能产生数据不一致的问题。
10 生产者与消费者问题
生产者 往容器类生产产品 ,直到到达容器的上限必须停止生产,等待消费者的消费。
消费者消费容器中的产品,直到容器中的产品消费完了,等待生产者生产产品。
代码实现:
%产品类 class Goods { int id; Goods(int id) { this.id = id; } public String toString() { return "Goods"+id; } } %放产品的堆栈 相当于一个容器,由于消费者和生产者都要对容器进行操作,因此必须对多个线程共同访问的资源枷锁 class SyncStack { int index=0; Goods arrGoods[] = new Goods[6];%只能放6个产品 %Push(Goods good)往容器中放产品的方法 public Synchronized void Push(Goods good) { while(index==arrGoods.length)%如果容器已满,就等待 { try { this.wait(); }catch(InterruptedException e){} } this.notify();%否则容器中有空位,放入产品 arrGoods[index]=good; index++; } %Pop()返回的是消费的产品 public synchronized Goods Pop() { while(index==0)%如果容器中没有产品了,就等待 { try { this.wait(); }catch(InterruptedException e){} } this.notify();%容器中有产品就可以消费产品 index--; return arrGoods[index]; } } %生产者只负责往容器中放入产品 class Producer implements Runnable { SyncStack ss = null; Producer(SyncStack ss) { this.ss = ss; } public void run() { for(int i=0;i<20;i++) { Goods goods = new Goods(i); ss.Push(goods); System.out.println("生产:"+goods); try { Thread.sleep(1000); }catch(InterruptedException e){} } } } %消费者只负责从容器中取走产品 class Consumer implements Runnable { SyncStack ss= null; Consumer(SyncStack ss) { this.ss = ss; } public void run() { for(int i=0;i<20;i++) { Goods goods = ss.Pop(); System.out.println("消费"+goods); try { Thread.sleep(2000); }catch(InterruptedException e){} } } } public class ProducerConsumer { public Static void Main(String args[]) { SyncStack ss = new SyncStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); Thread t1 = new Thread(p); Thread t2 = new Thread(c); t1.start(); t2.start(); } }分析:wait()和sleep()的区别
(1)在一个线程中任何地方都可以条用sleep()方法,当只有加锁的对象才能调用wait(),也就是说只有用Synchronized的地方才能用wait();
(2)wait()是object类的方法,而sleep()是Thread类的方法;
(3)加锁的对象调用了wait()后,这个线程就不在拥有锁了,只有被 notify()叫醒后,才能找回该锁,而加锁的对象调用了sleep()后,这个线程依然用后该锁;
(4)wait()必须用notify()才能被叫醒,而sleep(1000),当过1000毫秒后就自动唤醒。
11.关于线程的一些基本的问题
java中有几种方法可以实现创建一个线程?
有两种方法创建一个线程:第一种方法是定义线程实现Runnable接口,调用的是Runnable接口的run()方法,即要重写Runnable接口的run()方法(推荐使用)
第二个方法是定义线程继承Thread方法,调用Thread父类的run()方法。
stop()和suspend()方法为什么不推荐使用?
(1)反对用Stop()方法,因为它不安全,它会解除由线程获取的所有锁,而且如果对象处于一种不连贯的状态,那么其他线程能在那种状态下检查和修改它们。
(2)反对用Suspend()方法,因为此方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然保持在此之间获得锁。因此其他任何线程都不能 访问锁定的资源,除非被suspend()挂起的线程状态恢复。
对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。
因此不应该使用suspend(),若线程要挂起就使用wait()方法,命其进入等待状态,因为加锁的对象调用了wait()后,此线程就不在拥有锁了,只有被notify()唤醒,才能找回该锁。
sleep()和wait()方法的区别?
线程同步和异步有何异同,分别在什么情况下使用它们?
(1)如果数据将在线程间共享,例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程谢过了 ,那么这些数据是共享数据,必须进行
同步读取。
(2)当应用程序在对象上调用了一个需要很长时间执行的方法,并不希望让程序等待该方法的返回值,这时应该异步编程,异步的效率更高。
当一个线程进入一个对象的Synchronized方法后,其他线程是否可以进入此对象的其他方法?
要分几种情况来讨论:
(1)如果此对象的其他方法前没有加Synchronized,就可以访问这些方法。
(2)如果此对象的其他方法前加了Synchronized,但是当前Synchronized方法内部 有wait(),则这个线程可以进入其它加了Synchronized的方法。
(3)如果当前Synchronized方法内部没有wait(),则 这个线程不能进入加入其它的Synchronized方法内部。
线程有哪些基本的状态,这些状态之间的关系是什么?
一个程序可以有多个线程同时执行,每个线程都有关联要执行的代码,而每个程序至少 有一个线程,即Main主线程。
当一个线程start()后,该线程就进入了就绪状态,等待CPU的调度,调度后就绪状态的线程就变成运行状态。
在运行 状态线程遇到某个对象的Synchronized语句时,但又获得不了LOCK时候,就由运行状态变成阻塞状态。
当Synchronized获得锁LOCK后,线程再由阻塞状态变成运行状态,同时可以使用wait ()转成挂起状态。
当线程相关代码执行完成后,线程变为结束状态。
相关文章推荐
- Java-J2SE学习笔记-线程-生产者消费者问题
- 线程通信,生产者消费者问题(Java)
- JAVA线程中的生产者和消费者问题
- java基础---->线程 生产者消费者问题
- Java线程(三):线程协作-生产者/消费者问题
- java线程通信--生产者消费者问题
- Java线程(三):线程协作-生产者/消费者问题
- Java基础_线程_多线程_生产者消费者问题
- JAVA学习笔记(1)_____模拟线程通信之生产者消费者问题
- java线程模拟生产者消费者问题
- Java线程(三):线程协作-生产者/消费者问题
- java再复习——线程的经典问题-生产者消费者
- 线程通信,生产者消费者问题(Java)
- Java线程间的相互通信--生产者消费者问题
- java 线程间通信,多生产者多消费者问题
- 生产者消费者问题之Java线程实现
- Java线程(三):线程协作-生产者/消费者问题
- Java线程(三):线程协作-生产者/消费者问题
- 线程的相关知识、JAVA实现死锁、生产者消费者问题
- Java线程(三)----线程协作-生产者/消费者问题