您的位置:首页 > 编程语言 > Java开发

Java 线程的基本概念(模仿生产者与消费者的问题)

2012-07-22 14:10 357 查看
1.什么是线程?

程序中一个单一顺序的控制流程。

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 ()转成挂起状态。
当线程相关代码执行完成后,线程变为结束状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: