在多任务系统中,每个独立执行的程序称为进程,也就是“正在进行的程序”。我们现在在使用的操作系统一般都是多任务的。即能够同时执行多个应用程序,实际情况是,操作系统负责对CPU等设备的资源进行分配和管理,虽然这些设备某一时刻只能做一件事,但是以非常小的间隔交替执行多个程序,就可以给人以同时执行多个程序的感觉。
一个进程中又可以包含一个或多个线程,一个线程就是一个程序内部的一条执行线索,如果要一程序中实现多段代码同时交替运行,就需要产生多个线程,并指定每个线程上所要运行的程序代码段,这就是多线程。
用Thread类创建线程
l 要将一段代码在一个新的线程上运行,该代码应该在一个类的run函数中,并且run函数所在的类是Thread类的子类。倒过来看,我们要实现多线程,必须编写继承了Thread类的子类,子类要覆盖Thread类中的run函数,在子类的run函数中调用想在新线程上运行的程序代码。
l 启动一个新的线程,我们不是直接调用Thread的子类对象的run方法,而是调用Thread子类对象的start(从Thread类继承到的)方法,Thread类对象的start方法将产生一个新的线程,并在该线程上运行该Thread类对象中的run方法,根据面向对象的运行时的多态性,在该线程上实际运行的是Thread子类(也就是我们写的那个类)对象中的run方法。
l 由于线程的代码段在run方法中,那么该方法执行完成以后线程也就相应结束了,因而我们可心通过控制run方法中循环的条件来控制线程的结束。
Thread类,这个类代表线程类,一个线程类对象代表一个线程
Thread.currentThread()方法返回当前的线程类对象
Thread.currentThread().getName()方法返回线程类对象的名称
创建一个线程的方法:
l class TestThread extends Thread
{
public void run( )
{
System.out.println(“你好!”);
}
}
class RunThread()
{
public static void main(Stringargs[ ])
{
new TestThread().start( );
}
}
- 如果我们对某个线程对象在启动(调用start方法)之前调用了这个线程对象的setDaemon(true)方法,这个线程就变成了后台线程。反之如果没有调用setDaemon方法,或者调用了setDaemon(false)则为前台线程。如:
TestThread tt=new TestThread( );
tt.setDaemon(true);
tt.start();//像这里的线程就是后台线程 - 对java程序来说,只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程运行,这个进程就会结束。
- pp.join()的作用是把pp所对应的线程合并到调用pp.jion();语句的线程中。也可在pp.jion()中加入毫秒,如pp.join(10000);//表示合并10秒种后又分开//这个语句要抛出异常
用Runnable对象创建多线程:
l class TestThread implements Runnable
{
public void run( )
{
System.out.println(“你好!”);
}
}
class RunThread()
{
public static void main( Stringargs[ ] )
{
new Thread(new RunThread()).start();
}
}
使用Runnable接口创建多线程的好处:
l 适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想
l 可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable
l 当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。
l 实事上,几乎所有多线程应用都可用Runnalbe接口方式。
多线程在实际中的应用
l 网络聊天程序的收发
l 表记录的复制的中途取消
l WWW服务器为每一个来访者都建立专线服务
多线程的同步
l 什么是线程安全
l 同步代码块
l 同步函数
l 代码块与函数间的同步
l 死锁问题
Thread.sleep(10);//线程暂停10毫秒,这个语句也会抛出异常
synchronized( ){ } //同步线程语句,把需要同步的代码放在{ }中即可 这个方法里的参数必需要有,参数为随意的一个对象
实例:
让4个线程同时卖100张票:
1、使用同步代码块
class ThreadDemo1
{
public staticvoid main(String [] args)
{
ThreadDemo2 tt=new ThreadDemo2();
newThread(tt).start();
newThread(tt).start();
newThread(tt).start();
newThread(tt).start();
}
}
class ThreadDemo2 implements Runnable
{
int tickets=100;
String str=newString();
public voidrun()
{
boolean bl=true;
while(bl)
{
synchronized(str)//str的标志位(也称为锁旗标)为1的时候进入代码块,代码块执行完的时候标志就会变为0,当给一个线程检查到标志位为0的时候就不会执行同步代码块
{
if(tickets> 0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--);
}
if(tickets<=0)
bl=false;
}
}
}
}
2、使用同步函数
class ThreadDemo1
{
public staticvoid main(String [] args)
{
ThreadDemo2tt=new ThreadDemo2();
newThread(tt).start();
newThread(tt).start();
newThread(tt).start();
newThread(tt).start();
}
}
class ThreadDemo2 implements Runnable
{
int tickets=100;
boolean bl=true;
String str=newString();
public voidrun()
{
while(bl)
{
sale();
}
}
public synchronizedvoid sale()
{
if(tickets> 0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--);
}
if(tickets<=0)
bl=false;
}
}
通过以上程序可以发现使用了同步语句执行时要比执行没有同步的代码要慢,这是因为系统要不停地对监视器进行检查,需要额外的开销,这说明同步是以牺牲程序的性能为代价的
3、同步代码块与同步方法之间的同步:
同步方法默认使用的监视对象是this对象,所以只要同步代码块也使用this就可以实例同步,实例如下:
class ThreadDemo1
{
public static void main(String [] args)
{
ThreadDemo2 tt=new ThreadDemo2();
new Thread(tt).start();
tt.str=new String("hello");
new Thread(tt).start();
tt.str=new String("");
new Thread(tt).start();
tt.str=new String("hello");
new Thread(tt).start();
}
}
class ThreadDemo2 implements Runnable
{
int tickets=100;
boolean bl=true;
String str=new String();
public void run()
{
if(str.equals("hello"))
{
while(bl)
{
synchronized(this)//如果这里改为str 对象则不会同步
{
if(tickets > 0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--);
}
}
}
}
else
{
while(bl)
{
sale();
}
}
}
public synchronized void sale()// 同步方法默认使用的监视对象是this对象
{
if(tickets > 0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--+"---------");
}
}
}
4、死锁问题实例:
class ThreadDemo1
{
public static void main(String [] args)
{
ThreadDemo2 tt=new ThreadDemo2();
new Thread(tt).start();
tt.str=new String("hello");
new Thread(tt).start();
tt.str=new String("");
new Thread(tt).start();
tt.str=new String("hello");
new Thread(tt).start();
}
}
class ThreadDemo2 implements Runnable
{
int tickets=100;
boolean bl=true;
String str=new String();
public void run()
{
if(str.equals("hello"))
{
while(bl)
{
synchronized(str)
{
if(tickets > 0)
{
try{Thread.sleep(10);}catch(Exception e){}
synchronized(this){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--);
}
}
}
}
else
{
while(bl)
{
sale();
}
}
}
public synchronized void sale()
{
if(tickets > 0)
{
try{Thread.sleep(10);}catch(Exception e){}
synchronized(str){}
System.out.println(Thread.currentThread().getName()+":正在卖火车票:"+tickets--+"---------");
}
}
}