您的位置:首页 > 职场人生

黑马程序员-多线程

2013-12-21 18:01 260 查看
-------android培训java培训、期待与您交流!
----------

进程与线程
进程是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程。

执行java程序时在系统的任务管理器窗口下能看到进程:java.exe。该进程中至少有一个线程负责java程序的执行(主线程),由于java虚拟机自动垃圾回收机制,所以还有负责垃圾回收机制的线程也会启动。

创建线程的两种方式
继承Thread类,步骤如下

创建类继承Thread类;

复写Thread类中的run()方法;

调用线程的start()方法(该方法有两个作用:启动线程,调用run()方法);

注意:启动线程必须调用start()方法,Thread类用于描述线程,该类定义的run()方法,用于存储线程要运行的代码,如果仅仅是调用run()方法,那么就成了简单的对象调用方法,而线程创建了却没有启动。

代码演示
class ThreadDemo extends Thread
{
ThreadDemo(){}

ThreadDemo(String name){
super(name);
}

//覆盖Thread类中的run()方法
public void run(){
for (int i=0;i<10 ;i++ )
{
//打印结果
System.out.println(this.getName()+":"+i+"run()");
}
}
public static void main(String[] args)
{
//创建线程t1
ThreadDemo t1=new ThreadDemo("t1");
//创建线程t2
ThreadDemo t2=new ThreadDemo("t2");
//开启线程t1
t1.start();
//开启线程t2
t2.start();
//打印主线程的运行结果
for (int i=0;i<10 ;i++ )
{
System.out.println("main:+"+i);
}
}
}
结果
main:+0
t1:0run()
t2:0run()
t1:1run()
main:+1
t1:2run()
t2:1run()
t1:3run()
main:+2
main:+3
t1:4run()
t1:5run()
t1:6run()
t1:7run()
t2:2run()
t1:8run()
main:+4
t1:9run()
t2:3run()
t2:4run()
main:+5
t2:5run()
main:+6
main:+7
main:+8
main:+9
t2:6run()
t2:7run()
t2:8run()
t2:9run()


通过比较多次运行的结果可以发现:每次的结果都不相同。这是因为CPU的工作原理所造成的。CPU在执行任务时,它是一个线程一个线程的执行。但是我们平常使用电脑会开启很多线程,并没有这种感觉是因为CPU在做着快速的切换,以达到看上去是同时运行的效果。CPU执行到谁,谁就运行。我们可以形象的把多线程的运行看做是在互相抢夺CPU的执行权。这就是多线程的一个特性:随机性。谁抢到CPU的执行权,那么谁就被执行。

实现Runnable接口
创建类实现Runnable接口。

子类覆盖接口中的run方法。将线程要运行的代码存放在该run()方法中。

通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。

Thread类对象调用start()方法开启线程,start()方法内部自动会调用Runnable接口子类的run()方法
演示代码
public class TestRunnable implements Runnable {
String name;

public TestRunnable(String name){
this.name = name;
}

//重写run方法
public void run() {
for(int i = 0; i < 10; i ++){
System.out.println(this.name + i);
}
}
//创建两个线程,并start()启动
public static void main(String[] args) {
TestRunnable tr1 = new TestRunnable("tr1:");
TestRunnable tr2 = new TestRunnable("tr2:");
new Thread(tr1).start();
new Thread(tr2).start();
}
}
运行结果:
tr1:0
tr2:0
tr1:1
tr2:1
tr1:2
tr1:3
tr2:2
tr1:4
tr2:3
tr1:5
tr2:4
tr1:6
tr2:5
tr1:7
tr2:6
tr1:8
tr2:7
tr1:9
tr2:8
tr2:9


实现Runnable接口创建线程避免了单继承的局限性。

多线程的同步
多线程的安全问题:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,CPU切换给另一个线程参与进来执行,就会导致共享数据的错误。
导致安全问题的出现的原因:

多个线程访问出现延迟。

线程的随机性 。

解决办法:
同步代码块,对多条线程操作共享数据的语句,只能让一个线程执行完,再让其它线程继续执行。
格式:

synchronized(对象)

{

需要同步的代码;

}
同步的前提:同步需要两个或者两个以上的线程,且必须使用的是同一个锁。

同步的好处:解决了多线程的安全问题。

同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

同步函数:在函数上加上synchronized修饰符即可。例:public synchronized void test(){}。

注:根据上面定义同步函数的格式可以看到同步函数中没有定义锁,那么同步函数使用的是哪一个锁呢?

我们知道函数需要被对象调用,那么函数都有一个所属对象引用:this,所以同步函数使用的锁是this。

如果同步函数被静态修饰后,使用的锁又是什么呢?

我们知道静态成员初始化时,内存中还没有本类对象,但是一定有本类对应的字节码文件对象,即:类名.class。该对象的类型是Class类。所以,静态的同步函数,使用的是该函数所在类的字节码文件对象。

线程间的通信:线程间的通信其实就是多个线程在操作同一个资源,但是操作的动作不同。

例如:一个Person类,有name属性,一个线程可以向name属性中赋值,一个线程可以从name属性中取值。
等待唤醒机制:在操作同步中的线程时,必须标识所操作线程持有的锁。只有同一个锁上的被等待线程才可以被同一个锁上的notify()方法唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。

停止线程:

定义循环结束标记。

因为想要让线程停止,就必须让run()方法结束。开启多线程运行,线程运行代码一般都是循环,只要控制了循环即可。

使用Thread类的interrupt(中断)方法。

该方法是结束线程的冻结状态,使线程回到运行状态中来。

注:java中提供的stop()停止线程的方法已经过时不再使用。

注:参照了网上的一些内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: