------- android培训、java培训、期待与您交流! ----------
一、线程
在计算机中,每一个程序的运行,都是通过线程来实现的。我们可以把一个正在执行的程序称为进程,每一个进程的执行都有一个执行顺序。该顺序是一个执行路径,或者叫控制单元。线程就是进程中的一个独立的控制单元,线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。一个进程中至少有一个线程。
在多个程序运行时,CPU会随机地在多个进程中快速切换,哪个线程抢到了CPU的执行权,哪个线程就运行。运行Java程序时,虚拟机启动会有一个java.exe的进程,该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,称之为主线程。虚拟机启动除了执行主线程外,还有负责垃圾回收机制的线程。这种在在一个进程中有多个线程执行的方式,就是多线程。
多线程的出现能让程序产生同时运行的效果,提高程序的运行效率。例如:在虚拟机启动后,进程中执行主线程时,一般会根据程序代码,在堆内存中产生很多对象,而对象调用完后,就成了垃圾,如果不及时清理,垃圾过多容易造成内存不足,影响程序的运行。所以如果只有主线程运行,程序的效率可能会很低;而如果有一个负责垃圾回收机制的线程运行时,就会对堆内存中的垃圾进行清理,保证了内存的稳定,就保证了程序的运行效率。
二、创建线程
有两种创建线程的方式:继承Thread类和实现Runnable接口。
(1)继承Thread类
Thread类是Java提供的对线程这类事物描述的类,通过继承Thread类,复写其run方法来创建线程。步骤如下:
1.定义一个类继承Thread。
2.覆盖Thread中的run方法。将自定义的代码放在run方法中,让线程运行时,执行这些代码。
3.创建这个类的对象。相当于创建一个线程。然后用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。注意如果直接用对象调用run方法,相当于没有启动创建的线程,还是只有主线程在执行。
覆盖run方法的原因:Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就是run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。
class MyThread extends Thread
{
//覆盖父类的run方法,存入运行代码
public void run(){
for(int x=0;x<1000;x++)
System.out.println(Thread.currentThread().getName()+"在运行");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//创建线程
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
//启动线程
mt1.start();
mt2.start();
for(int x=0;x<1000;x++)
System.out.println("Hello World!");
}
}
(2)实现Runnable接口
实现Runnable接口,覆盖run方法。这种创建线程的方式避免了单继承的局限性,在定义创建线程时,一般都使用这种方式。具体步骤如下:
1.定义一个类实现Runnable的接口。
2.覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法。这样做是因为自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。
4.调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。
class myRunnable implements Runnable
{
public void run()
{
for(int i=0;i<5;i++)
{
System.out.println(Thread.currentThread().getName()+"....running : "+ i);
}
}
}
class RunnableDemo
{
public static void main(String[] args)
{
myRunnable t = new myRunnable();
//创建4个线程
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
三、线程的运行状态
被创建:等待启动,调用start启动。如果线程已经启动,处在运行时,再次调用start方法,没有意义,会提示线程状态异常。
运行状态:具有执行资格和执行权。
临时状态(阻塞):有执行资格,但没有执行权。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
消亡状态:stop()方法,或者run方法结束。
四、线程安全
当多条语句在操作多个线程的共享数据时,当一个线程对多条语句只执行了一部分,还没有执行完时,另一个线程可能就会参与进来执行,这样会导致共享数据的错误。线程的安全问题一旦出现对程序的影响很大。所以Java中提供了解决线程安全问题的方法,叫做同步(synchronized),就是对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行,这样就解决了线程的安全问题。同步中分为两种解决方法,一种是同步代码块,另一种是同步函数
(1)同步代码块
格式:synchronized(对象){需要被同步的代码}
(2)同步函数
格式:就是在函数上加上synchronized即可。因为非静态函数需要被对象调用,所以非静态函数中都有一个所属对象引用,即this。
同步例子:class Ticket implements Runnable
{
private int ticket = 100;
public void run()
{
while(true)
show();
}
//通过同步函数,实现同步。
public synchronized void show()
{
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"....sale : "+ ticket--);
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}