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

菜鸟猿大战Java之线程初接触

2015-11-04 23:27 441 查看

1.线程的概述

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

2.线程的定义:进程中的一个独立的控制单元,线程控制着进程的执行。

3.进程中至少有一个线程(且java中该线程的代码存在于main方法中,因此称其为主线程)

4.使用多线程的意义:

-实现同时运行的目的

-提高下载效率[如下载工具某雷]

5.一个新的线程就是一个新的执行路径,CPU快速不停地在各个线程间进行切换(表现为各线程抢夺执行权),以达到看似同时运行的效果

多线程的一个特性:随机性

2.创建线程

1.继承Thread类:

步骤:

-定义一个类继承
Thread


-复写
Thread
类中的
run
方法 ,
run
中放置需要在线程中运行的代码

-实例化该类,调用
start
方法[启动线程,调用run方法]

说明:若直接调用
run
方法,则没有启动线程。程序依旧是单线程运行

说明:在继承
Thread
的子类构造方法中注意
super()
的使用

示例代码:

//自定义线程类
class MyThread extends Thread{
public MyThread(String name){
super(name);//直接使用
}
//复写run方法
public void run(){
for(int i=0;i<50;i++){
System.out.println(this.getName()+" run"+i);
}
}
}

public class ThreadManager {

public static void main(String[] args) {
MyThread thread=new MyThread("新的线程");//新建线程
thread.start();//启动线程
}

}


2.实现Runnable接口:

步骤:

- 定义类实现
Runnable
接口[
implements
关键字]

- 复写
Runnable
接口中的
run
方法

- 通过
Thread
类创建新的线程(将实现
Runnable
接口的子类对象作为实参传递给
Thread
的构造方法)

- 调用
Thread
中的
start
方法开启线程并调用
Runnable
接口子类中的
run
方法

编程思想:如果程序员只需要重写
thread
中的
run
方法,而不重写其他
thread
方法,应该实现
runnable
接口。因为除非程序员打算修改或增强类的基本行为,否则不应该为该类创建对象。

示例代码:

//实现Runnable的线程类
class MyNewThread implements Runnable{

public void run() {
System.out.println("线程执行...");
}

}

public class ThreadManagerByIm {

public static void main(String[] args) {
MyNewThread thread=new MyNewThread();
Thread newThread=new Thread(thread);//以Runnable的实现类为实参实例化Thread
newThread.start();//启动线程
}

}


3.实现Runnable方式与继承Thread方式创建线程的区别

实现Runnable方式可以避免单继承的局限性[!important]

实现Runnable方式使得对象中的资源可以被共享

两种方式下线程代码的存放的位置有所不同

3.线程的状态

1.线程对象及名称

Thread currentThread()

返回对当前正在执行的线程对象的引用

String getName()

返回该线程的名称

void setName(String name)

改变线程名称,使之与参数 name 相同

2.线程状态一览



线程状态说明
新建线程新建
就绪线程运行
start
方法
运行线程获得CPU资源,即获得CPU执行权
阻塞线程处于等待或休眠状态,线程此时具有具备运行资格但没有执行权
死亡线程彻底结束

4.线程安全性问题

1.实例引入

//售票窗口类
class TicSale implements Runnable{
private int ticket=100;//票数
@Override
public void run() {
while(true){
if(ticket>0){
try{
//令线程短暂休眠
Thread.sleep(10);
}catch(Exception e){}                System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
}
}
}

}

public class SaleTic {

public static void main(String[] args) {
TicSale ts=new TicSale();
//用两个线程模拟双窗口售票
Thread t1=new Thread(ts);
Thread t2=new Thread(ts);
t1.start();
t2.start();
}

}


运行结果:





Thread-0当前售出票号:2

Thread-1当前售出票号:2

Thread-0当前售出票号:1

Thread-1当前售出票号:1





在本例中,由于令售票线程短暂休眠,导致最终售票的结果出现异常情况。由此,可以体会到当多线程对共享数据进行操作时,存在一定的安全性问题。

2.出现安全性问题的原因

当多条语句在操作同一个共享数据时,一个线程对多条语句只执行了一部分(如中途进入休眠状态),另一个线程参与进来执行,就将导致共享数据的错误。

一个可行的解决方案:对多条操作共享数据的语句,只有当一个线程执行完,其他线程才能执行

3.多线程同步代码块

格式:

synchronized(被操作的共享资源所属的对象){
//需要被同步的代码(操作共享数据的语句)
}


说明:需要被同步的对象一般可称为同步锁,一般为
this
或是
被操作的共享资源所属的对象
(理论上任何对象都可以)。持有锁的线程可以在同步中执行,没有持有锁的线程即使获取了CPU的执行权,也无法进入同步代码块进行执行。

示例代码段:

public void run() {
while(true){
synchronized(this){
if(ticket>0){
try{
//令线程短暂休眠
Thread.sleep(10);
}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
}
}
}
}


4.多线程同步方法

格式:在普通方法前面添加synchronized关键字(此时同步锁是this)

示例代码段:

public synchronized void sale(){
if(ticket>0){
try{
//令线程短暂休眠
Thread.sleep(10);
}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
}
}


5.进行同步的前提及弊端

前提:

程序必须要有两个或两个以上的线程

必须是多个线程使用同一个锁(尤其体现在同步方法与同步代码块中,因此将同步代码块的锁改为
this


弊端:

- 多个线程需要判断锁,会消耗更多系统资源

6.分析多线程安全问题的方法:

明确哪些代码是多线程运行代码(
run
中的代码及包含的方法)

明确哪些是共享数据

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

7.静态同步方法

说明:

如果同步方法被静态修饰后,使用的锁是该方法所在类的字节码文件对象[
类名.class
获取]

分析

静态方法进入内存时,内存中没有本类的实例对象。但是有该类对应的字节码文件对象。

通过
类名.class
获取 ,该对象的类型是Class
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: