菜鸟猿大战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
相关文章推荐
- Synchronized和java.util.concurrent.locks.Lock的区别详解
- java关于==与equals的讨论
- Elasticsearch 基本介绍和安装
- Java中从控制台输入数据的几种常用方法
- Java记录 -59- SortedSet
- java.lang.ClassCastException
- java中volatile关键字的含义
- No result defined for action org.strutsOne.web.SumAction and result
- Java记录 -58- Iterator 迭代器
- SpringAop_切入点表达式 例子 原理
- java.sql.BatchUpdateException:
- Retrofit 入门--part2
- for遍历集合(jdk1.5推出来之后用最新的版本
- Java方法的重载和类的继承
- struts2表单重复提交
- Java 日志管理最佳实践
- MyEclipse6.5中SVN插件安装
- eclipse一直卡住,出现 “android sdk content loader 0%” 卡住的错误分析及解决方法
- SpringIoC依赖注入的过程(三)
- 【JDK源码】JDK/ArrayList源码逐行详解