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

黑马程序员——多线程技术

2015-08-02 21:04 429 查看
——- android培训java培训、期待与您交流! ———

1.进程,线程,多线程的概念:

进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

2.线程是指进程中的一个执行流程,一个进程中可以运行多个线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

3.java JVM启动的时候就会有一个进程java.exe.

该进程中至少一个线程负责java程序的执行

而且该线程运行的代码存在于main()方法中。

该线程称为主线程。

JVM启动的时候,不止一个线程,还有负责垃圾回收机制的线程。

在自定义代码中,自定义一个多线程的放式:

创建线程的第一种方式:继承Thread类。

步骤:

1.定义类继承Therad.

2.覆写Thread类中的run方法

目的:将自定义代码存储带run方法中,让线程运行。

3.调用现场start方法。

该方法两个作用:启动线程,调用run方法。

没次运行结果会有不同

因为多个线程都有获取cpu的执行权,cpu执行到谁,谁就运行。

(多核除外)在某一个时刻。只能有一个程序在运行,CPU在做着快速的切换

多线程的运行行为在互相抢夺。

Thread类用于描述线程。

该类定义了一个功能。用于存储线程要运行的代码。该存储功能就run方法

代码示例:

class  Demo extends Thread
{
public void run()
{
for(int x=0;x<60;x++)
System.out.println("demo run-------"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d=new Demo();//创建一个线程
d.start();//启动线程,调用父类的方法,覆盖运行子类的run
//d.run();//仅仅是对象调用方法。而线程创建了,但没有运行
for(int x=0;x<60;x++)
System.out.println("hello Words! --"+x);
}
}




需求:创建两个线程。和主线程交替运行;

代码示例:

class  Test extends Thread
{
private String name;
Test(String name)
{
this.name=name;
}
public void run()
{
for(int x=0;x<60;x++)
System.out.println(name+" run-------"+x);
}
}
class ThreadTestDemo
{
public static void main(String[] args)
{
Test t1=new Test("one");
Test t2=new Test("two");
t1.start();
t2.start();

for(int x=0;x<60;x++)
System.out.println("main()---"+x);
}
}


线程的状态和转换:如图所示



1、被创建:线程对象已经创建,还没有在其上调用start()方法。

2、可运行(临时状态:有执行资格但没有执行权):当线程有格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。

3、运行状态(有资格,有执行权):线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

4、冻结状态(等待/阻塞/睡眠状态:没有执行资格):这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。

5、消亡态:当线程的run()方法结束时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

获取和设置线程名称

原来线程都有自己默认的名称

Thread-编号 该编号默认从0开始

static Thread currentThread():获取当前线程对象。

getName();获取线程名称

设置线程名称:setName或者构造函数

代码示例:

class  Test extends Thread
{
private String name;
Test(String name)
{
//this.name=name;
super(name);
}
public void run()
{
for(int x=0;x<60;x++)  //返回的是线程对象。getName()//Thread.currentThread()标准通用方式
System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run-------"+x);
//System.out.println(this.getName()+" run-------"+x);
}
}
class ThreadTestDemo
{
public static void main(String[] args)
{
Test t1=new Test("one");
Test t2=new Test("two");
t1.start();
t2.start();

for(int x=0;x<60;x++)
System.out.println("main()---"+x);
}
}


创建线程的第二种方式:实现Runnable接口

步骤:

1.定义类实现Runnable接口

2.覆盖Runnable接口中的run方法

将线程要运行的代码存放在run方法中

3.通过Thread类建立线程对象。

4.将Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。

为什么奖Runnable接口的子类对象传递给Thread的构造函数

因为自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定对象的run方法。就必须明确该run方法所属的对象。

5.调用Thread类中的Start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式的区别:

1.实现方式好处:避免了单继承的局限性

在定义线程时候,建议使用实现方式

并运用了多态的形式建立了接口的使用

2.继承Thread:线程代码存放在Thread子类run方法中

实现runnable;线程代码存放在接口子类的run方法中。

实现方式示例:

class Ticket implements Runnable
{
private int tick =60;
Object obj=new Object();
public void run()
{
//synchronized (obj) //while(tick>0)
while(true)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"sale :"+tick--);
}
}
}
}
class  TicketDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();//创建一个Ticket对象
//Thread(Runnable target)
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();
}
}


多线程的安全问题

多线程安全问题

导致共享数据的错误

当多条语句在操作同一个线程共享数据时。一个线程对多条语句只执行了一部分。

还没有执行完,另一个线程参与进来,导致共享数据的错误。

解决办法:

对多条操作操作共享数据的语句,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。

java对于多线程安全问题提供了专业的解决方式:同步

同步代码块

synchronized(对象)

{

需要被同步的代码(操作共享数据的语句)

}

示例:

Object obj=new Object();

while(true)

{

synchronized (obj)//封装同步代码块

{

if(tick>0)

{

System.out.println(Thread.currentThread().getName()+”sale :”+tick–);

}

}

}

同步代码块的前提

1.必须要有两个或者两个以上的线程。

2.必须是多个线程使用同一个锁。

必须要做同步中只能有一个线程在运行。

弊端:多个线程需要判断,较为消耗资源。

如何判断那些需要被同步(找出安全问题隐患)

1.明确那些代码是多线程运行代码。

2.明确共享数据。

3.明确多线程运行代码中那些语句是操作共享数据的。

同步函数也用synchronized修饰

同步代码块:

Object obj=new Object();
public void add(int n)
{
synchronized(obj)
{
sum=sum+n;
try
{
Thread.sleep(10);
}
catch(Exception e)
{
throw Exception("休眠异常!");
}
}
}


转换成同步函数:(同步函数使用的锁是this)

public synchronized void add(int n)
{
sum=sum+n;
try
{
Thread.sleep(10);
}
catch(Exception e)
{
throw Exception("休眠异常!");
}
}


注意:如果同步函数被静态修饰后,使用的锁不是this(由于静态方法中不可以定义this)

静态方法由类调用,类被封装成class对象

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

静态同步函数的锁是:类名.class

死锁

死锁通常出现在同步中嵌套同步,但是锁不同

死锁的原因一般是两个对象的锁相互等待造成的。

示例:

class Test extends Thread
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
synchronized (MyLock.locka)
{
System.out.println("if locka");
synchronized (MyLock.locka)
{
System.out.println("if lockb");
}
}
}
else
{
synchronized (MyLock.lockb)
{
System.out.println("else laockb");
synchronized (MyLock.lockb)
{
System.out.println("else laoca");
}
}
}
}
}
class MyLock
{
static Object locka=new Object();
static Object lockb=new Object();
}
class DeadLock
{
public static void main(String[] args)
{
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(false));
t1.start();
t2.start();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: