----------android培训、java培训期待与您交流!----------
进程与线程的简介
进程是程序的一次动态执行,它经历了从代码加载,执行到执行完毕的一个完整的过程,这个过程也是进程本身从产生,发展到最终消亡的过程。多进程操作系统能同时运行多个进程(程序),由于cpu具备分时机制,所以每个进程都能循环获得自己的cpu时间片。由于cpu执行速度快,使得所有程序好像是同时运行一样.
多线程是实现并发机制的一种有效手段。进程和线程一个,都是实现并发的一个基本单位。线程是比进程更小的执行单位,线程是在进程的基础上进行的进一步划分。所谓的多线程是指一个进程在执行过程中,可以产生多个线程,这些线程可以同时存在,同时运行,一个进程可能包含多个同时执行的线程,
java中线程的实现
在java中,要想实现多线程有2中代码,一种是继承Thread类,另外一种是实现Runnable接口.
继承Thread类
Thread类实在java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程操作类。在Thread类,必须明确的覆写Thread类中的run()方法,此方法称为线程的主体
语法定义
class 类名称 extends Thread{//继承Thread类
属性......;//类中定义属性
方法......;//类中定义的方法
public void run(){//覆写Thread类中的run方法,此方法时线程的主体
线程主体;
}
}
常用方法
Thread.currentThread() 获取当前线程对象
getName() 获取线程名称
setName() 设置线程名称
start() 使该线程开始执行;Java虚拟机调用该线程的 run 方法。
实例代码
class MyThread extendsThread{ // 继承Thread类,作为线程的实现类实例代码
private Stringname ; //表示线程的名称
public MyThread(String name){
this.name = name; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程的操作主体
for(inti=0;i<10;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
public class Demo{
public static void main(String args[]){
MyThread mt1 = newMyThread("线程A ") ; //实例化对象
MyThread mt2 = newMyThread("线程B ") ; //实例化对象
mt1.start(); // 调用线程主体
mt2.start(); // 调用线程主体
}
};
结果:
线程B 运行,i = 3
线程A 运行,i = 0
线程B 运行,i = 4
..........
...........
(输出结果每次都不一样,到底哪个先运行,是系统调度后做出的决定,谁先拿到cpu的执行权,谁先运行)
PS:在启动多线程时,必须通过start()方法启动,因为线程的运行需要本机操作系统的支持
实现Runnable接口方式实现多线程
在java中也可以通过实现Runnable接口的方式实现多线程,Ruannable中只定义了一个抽象方法
public void run();
使用Runnable接口实现多线程的格式如下
class 类名称 implements Runnable{//实现Runnable接口
属性......;//类中定义属性
方法......;//类中定义的方法
public void run(){//覆写Thread类中的run方法,此方法时线程的主体
线程主体;
}
代码实例
class MyThread implements Runnable{ //实现Runnable接口,作为线程的实现类
private String name; // 表示线程的名称
public MyThread(String name){
this.name = name; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程的操作主体
for(inti=0;i<10;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
public class RunnableDemo01{
public static void main(String args[]){
MyThread mt1 = newMyThread("线程A ") ; //实例化对象
MyThread mt2 = newMyThread("线程B ") ; //实例化对象
Thread t1 = newThread(mt1) ; // 实例化Thread类对象
Thread t2 = newThread(mt2) ; // 实例化Thread类对象
t1.start(); // 启动多线程
t2.start(); // 启动多线程
}
};
PS:以上两种方式,无论是员工那种方式,最终都必须依靠Thread类才能启动多线程
Thread类和Runnable接口的比较
1 实现了Runnable接口适合多个程序代码的线程去处理同一资源
2 可以避免由于java的但继承特性带来的局限
3 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
代码实例---多个线程执行卖票
class MyThread implements Runnable{ //继承Thread类,作为线程的实现类
private int ticket = 5; // 表示一共有5张票
public void run(){ //覆写run()方法,作为线程的操作主体
for (int i=0;i<100;i++){
if (this .ticket >0){
System. out .println("卖票:ticket = " + ticket --) ;//每卖掉一张票,票数减一
}
}
}
};
public class Demo{
public static void main(String args[]){
MyThread mt = new MyThread(); //实例化对象
new Thread(mt).run(); //调用线程主体
new Thread(mt).run(); //调用线程主体
new Thread(mt).run(); //调用线程主体
}
};
结果:卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
线程的状态
任何线程一般都具有5种状态,既创建(),就绪(执行start()方法),运行(获得cpu执行权,运行run()方法),阻塞(sleep(),wait()),死亡(调用stop方法或者run方法执行结束)
一些常用的线程操作方法
取得和设置线程名称
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()
+ "运行,i = " +i) ; //取得当前线程的名字
}
}
};
public class Demo{
public static void main(String args[]){
MyThread mt = new MyThread(); //实例化Runnable子类对象
new Thread(mt).start(); //系统自动设置线程名称
new Thread(mt,"线程A" ).start(); //手工设置线程名称
}
};
结果:Thread-0运行,i = 0
Thread-0运行,i = 1
Thread-0运行,i = 2
线程A运行,i = 0
线程A运行,i = 1
线程A运行,i = 2
判断线程是否启动
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
System. out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; //取得当前线程的名字
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //实例化Runnable子类对象
Threadt = new Thread(mt,"线程" ); //实例化Thread对象
System. out.println("线程开始执行之前 --> " + t.isAlive()) ; //判断是否启动
t.start(); //启动线程
System. out.println("线程开始执行之后 --> " + t.isAlive()) ; //判断是否启动
for(int i=0;i<3;i++){
System. out.println(" main运行 --> " + i) ;
}
//以下的输出结果不确定
System. out.println("代码执行之后 --> " + t.isAlive()) ; //判断是否启动
}
};
结果:
线程开始执行之前 --> false
线程开始执行之后 --> true
main运行 --> 0
线程运行,i = 0
main运行 --> 1
main运行 --> 2
线程运行,i = 1
代码执行之后 --> true
线程运行,i = 2
线程的强制运行
在线程操作中,可以使用join()方法让一个线程强制运行,此时其他线程无法运行,必须等待此线程完成之后才可以继续运行
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<5;i++){
System. out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; //取得当前线程的名字
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //实例化Runnable子类对象
Threadt = new Thread(mt,"join()方法测试线程" ); //实例化Thread对象
t.start(); //启动线程
for(int i=0;i<5;i++){
if(i>1){//如果i>1,就让t线程,获得cpu强制执行,此时main线程,要等待
try{
t.join(); //线程强制运行
} catch(InterruptedException e){}
}
System. out.println("Main线程运行 --> " + i) ;
}
}
};
结果:
Main线程运行 --> 0
Main线程运行 --> 1
join()方法测试线程运行,i = 0
join()方法测试线程运行,i = 1
join()方法测试线程运行,i = 2
join()方法测试线程运行,i = 3
join()方法测试线程运行,i = 4
Main线程运行 --> 2
Main线程运行 --> 3
Main线程运行 --> 4
线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用Thread.sleep()方法即可实现线程的休眠
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
try{
Thread. sleep(1000); //线程休眠1秒
} catch(InterruptedException e){}
System. out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; //取得当前线程的名字
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //实例化Runnable子类对象
Threadt = new Thread(mt,"线程" ); //实例化Thread对象
t.start(); //启动线程
}
};
结果:
线程运行,i = 0
线程运行,i = 1
线程运行,i = 2
每间隔一秒钟,打印一次。
线程的中断
当一个线程运行时,另外一个线程可以直接通过interrupt方法中断其运行状态
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
System. out.println("1、进入run()方法" ) ;
try{
Thread.sleep(10000); //线程休眠10秒
System. out.println("2、已经完成了休眠" ) ;
} catch(InterruptedException e){
System. out.println("3、休眠被终止" ) ;
return ; //返回调用处
}
System. out.println("4、run()方法正常结束" ) ;
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //实例化Runnable子类对象
Thread t = new Thread (mt,"线程" ); //实例化Thread对象
t.start(); //启动线程
try{
Thread.sleep(2000); //线程休眠2秒
} catch(InterruptedException e){
System. out.println("3、休眠被终止" ) ;
}
t.interrupt(); // 线程休眠2秒,中断线程执行
}
};
结果:1、进入run()方法
3、休眠被终止
线程的优先级
在java的线程操作中,所有的线程运行前都会保持就绪状态,那么此时,哪个线程的游戏优先级高,哪个线程就有可能会先被执行
lass MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<5;i++){
try{
Thread. sleep(500); //线程休眠
} catch(InterruptedException e){}
System. out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; //取得当前线程的名字
}
}
};
public class Demo{
public static void main(String args[]){
Threadt1 = new Thread(new MyThread(),"线程A" ) ; // 实例化线程对象
Threadt2 = new Thread(new MyThread(),"线程B" ) ; // 实例化线程对象
Threadt3 = new Thread(new MyThread(),"线程C" ) ; // 实例化线程对象
t1.setPriority(Thread. MIN_PRIORITY) ; //优先级最低
t2.setPriority(Thread. MAX_PRIORITY) ; //优先级最低
t3.setPriority(Thread. NORM_PRIORITY) ; //优先级最低
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程
}
};
结果:线程B运行,i = 0
线程A运行,i = 0
线程C运行,i = 0
线程B运行,i = 1
线程A运行,i = 1
线程C运行,i = 1
线程B运行,i = 2
线程A运行,i = 2
线程C运行,i = 2
线程B运行,i = 3程A运行,i = 3
线程C运行,i = 3
线程B运行,i = 4
线程A运行,i = 4
线程C运行,i = 4
PS:一定要注意,并非线程的优先级高就一定会先执行,哪个线程先执行将由cpu的调度决定
线程的礼让
在线程的操作中,使用yield()方法将一个线程的操作暂时让给其他的线程执行
class MyThread implements Runnable{ //实现Runnable接口
public void run(){ //覆写run()方法
for(int i=0;i<3;i++){
try{
Thread. sleep(500);//休眠0.5秒
} catch(Exception e){}
System. out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; // 取得当前线程的名字
if(i==2){//当i==2时,线程进行礼让
System. out.print("线程礼让:" ) ;
Thread.currentThread ().yield() ; //线程礼让
}
}
}
};
public class Demo
{
public static void main(String args[]){
MyThreadmy = new MyThread() ; //实例化MyThread对象
Threadt1 = new Thread(my,"线程A" ) ;
Threadt2 = new Thread(my,"线程B" ) ;
t1.start();
t2.start();
}
};
结果:
线程B运行,i = 0
线程A运行,i = 0
线程B运行,i = 1
线程A运行,i = 1
线程B运行,i = 2
线程礼让:线程A运行,i = 2
线程礼让:线程B运行,i = 3
线程A运行,i = 3
线程B运行,i = 4
线程A运行,i = 4
PS:join:当A线程执行到B线程的join()方法时,A就会等待,等待B线程执行完,A才会执行。join可以用来临时加入线程执行
同步与死锁
一个多线程的程序如果是通过Runnable接口实现的,则意味着类中的属性将要被多个线程共享,那么这样一来就会造成一个问题,如果这多个线程要操作同一资源有可能出现资源的同步问题。
代码实例
class MyThread implements Runnable{
private int ticket = 5 ; // 假设一共有5张票
public void run(){
for(int i=0;i<100;i++){
if(ticket >0){ //还有票
try{
Thread. sleep(500); //加入延迟。线程执行时,就有可能出现资源同步问题
} catch(InterruptedException e){
e.printStackTrace();
}
System. out.println("卖票:ticket = " + ticket-- );
}
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //定义线程对象
Threadt1 = new Thread(mt) ; //定义Thread对象。执行卖票
Threadt2 = new Thread(mt) ; // 定义Thread对象。执行卖票
Threadt3 = new Thread(mt) ; // 定义Thread对象。执行卖票
t1.start();
t2.start();
t3.start();
}
};
结果:
卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
卖票:ticket = 0
卖票:ticket = -1(有可能会出现ticket是负数的情况,属于资源同步问题)
如果要解决这样的问题,就必须使用同步。所谓的同步,就是指多个操作在同一时间段内只能有一个程序运行,其他线程都要等待此线程完成之后才可能继续执行
使用同步解决资源问题
解决资源共享的同步问题,可以使用同步代码块和同步方法两种方式来完成
同步代码块
synchronized(同步对象){
需要同步的代码
}
class MyThread implements Runnable{
private int ticket = 5 ; // 假设一共有5张票
public void run(){
for(int i=0;i<100;i++){
synchronized(this ){ //要对当前对象进行同步,关键字synchronized
if(ticket >0){ //还有票
try{
Thread. sleep(300); //加入延迟
} catch(InterruptedException e){
e.printStackTrace();
}
System. out.println("卖票:ticket = " + ticket -- );
}
}
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //定义线程对象
Threadt1 = new Thread(mt) ; //定义Thread对象
Threadt2 = new Thread(mt) ; //定义Thread对象
Threadt3 = new Thread(mt) ; //定义Thread对象
t1.start();
t2.start();
t3.start();
}
};
结果:卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
同步方法
除了可以将需要的代码块设置成同步代码块快,也可以使用synchronized关键字将一个方法声明成同步方法
synchronized 方法返回值方法名称(参数列表){
}
class MyThread implements Runnable{
private int ticket = 5 ; // 假设一共有5张票
public void run(){
for(int i=0;i<100;i++){
this.sale() ; //调用同步方法
}
}
public synchronized void sale(){ //声明同步方法
if(ticket >0){ //还有票
try{
Thread. sleep(300); //加入延迟
} catch(InterruptedException e){
e.printStackTrace();
}
System. out.println("卖票:ticket = " + ticket-- );
}
}
};
public class Demo{
public static void main(String args[]){
MyThreadmt = new MyThread() ; //定义线程对象
Threadt1 = new Thread(mt) ; //定义Thread对象
Threadt2 = new Thread(mt) ; //定义Thread对象
Threadt3 = new Thread(mt) ; //定义Thread对象
t1.start();
t2.start();
t3.start();
}
};
结果:卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
线程操作的经典案例学习-----生产者和消费者
生产者生产出信息后将其放到一个区域中,消费者从区域取出数据,但是因为涉及到线程运行的不确定性,可能会存在以下2个问题
1 假设生产者线程刚向数据存储空间添加了信息的名称,还没有加入该信息的内容,程序就切换到了消费者线程,消费者线程将把会把信息的名称和上一个信息的内容联系在一起
2 生产者放了若干次的数据,消费者才开始取数据,或者是,消费者取完一个数据后,还没等到射给生产者放入新的数据,又重复取出已经去过出的数据
通过代码,来分析上述问题的解决方式。主要是用到Object中,对线程的一些支持方法。
wait(),notify(),notifyAll();,都使用在同步中,因为都要对持有监视器(锁)的线程操作,所以要使用在同步中,而只有在同步中才具体锁。定义在Object类中,是因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程持有的锁。锁上的被等待线程,只可以被同一个锁上的notify或notifyAll唤醒,不可以被不同锁中的线程notify或notifyAll唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
一般来说,所有等待的线程会按照顺序进行排列,如果现在使用了notify()方法,则会唤醒第一个等待的线程,而如果使用了notifyAll方法,则会唤醒所有的等待线程,哪个线程优先级高,哪个线程就有可能先执行
class Info{ //定义信息类
private String name = "上午对应---"; //定义name属性
private String content = "am" ; //定义content属性
private boolean flag = false ; // 设置一个标志位为boolean类型变量,如果标志位的内容为true,则表示可以生产,但是不能取走,此时线程执行到了消费者线程则应该等待。如果标志位为false,则表示可以取走,但是不能生产,如果生产者线程运行,则应该等待
public synchronized void set(String name,String content){//生产者线程
if(!flag ){//如果flag为false,表示消费者可以取走,此时生产者需要等待
try{
super.wait() ;//生产者等待消费者取走数据
} catch(InterruptedException e){
e.printStackTrace() ;
}
}
this.setName(name) ; //设置名称
try{
Thread. sleep(300) ;
} catch(InterruptedException e){
e.printStackTrace() ;
}
this.setContent(content); //设置内容
flag = false ; //改变标志位,表示可以取走
super.notify() ;
}
public synchronized void get(){
if(flag ){//flag为true,表示可以生产,此时消费者线程等待
try{
super.wait() ;
} catch(InterruptedException e){
e.printStackTrace() ;
}
}
try{//加入延迟操作
Thread. sleep(300) ;
} catch(InterruptedException e){
e.printStackTrace() ;
}
System. out.println(this .getName() +
" --> " + this .getContent()) ;
flag = true ; //改变标志位,表示可以生产
super.notify() ;
}
public void setName(String name){
this.name = name ;
}
public void setContent(String content){
this.content = content ;
}
public String getName(){
return this .name ;
}
public String getContent(){
return this .content ;
}
};
class Producer implements Runnable{ //通过Runnable实现多线程
private Info info = null ; //保存Info引用
public Producer(Info info){
this.info = info ;
}
public void run(){
boolean flag = false ; //定义标记位
for(int i=0;i<50;i++){
if(flag){//如果标志位的内容为true,则表示可以生产
this.info .set("上午对应---" ,"am" ) ; // 设置名称
flag = false ;
} else{
this.info .set("下午对应---" ,"pm" ) ; // 设置名称
flag = true ;
}
}
}
};
class Consumer implements Runnable{
private Info info = null ;
public Consumer(Info info){
this.info = info ;
}
public void run(){
for(int i=0;i<50;i++){
this.info .get() ;
}
}
};
public class Demo{
public static void main(String args[]){
Info info = new Info(); //实例化Info对象
Producer pro = new Producer(info) ; //生产者
Consumer con = new Consumer(info) ; //消费者
new Thread(pro).start() ;
new Thread(con).start() ;
}
};
结果:上午 --> am
下午 --> pm
上午 --> am
下午 --> pm
上午 --> am
下午 --> pm(截取一部分,上午对应am,下午对应pm,数据正确)
小结:
1 线程是指程序的运行流程。多线程机制可以同时运行多个程序快,使得程序运行的效率更高
2 run方法时定义在Thread类中的一种fagfa,因此把线程的程序代码编写在run方法汇内所做的覆写的操作
3 Runnable接口中声明了抽象的run方法,因此必须在Runnable接口的类中明确定义run方法
4Thread类中的sleep方法可以用来控制线程的休眠状态,休眠的时间要看sleep中的参数而定
5 要强制运行某一线程,可以使用join方法
6 当多个线程对象操纵同一共享资源时,要使用 synchronized 关键字来进行资源的同步处理