Java复习(6)-多线程

1.什么是多线程(what)

1.1 线程

每个正在系统上运行的程序都是一个进程。每个进程至少包含一个线程。这里也要提下进程的概念。进程既是资源分配单位,又是调度和执行单位。线程是基本调度和执行单位。

1.2 多线程

一个进程里面跑多个线程。

1.3 线程的生命周期

线程的生命周期有创建,可执行,非可执行和消亡4个状态。
这里写图片描述
创建:
当创建(new)一个Thread对象并执行start()方法后,线程进入”可执行”状态(Runnable)。线程执行起来,虽然看起来像是同时在执行,但是事实上,同一时间里,只有一个线程在运行,只是线程之间的切换很快,所以感觉在同时运行。处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
可执行:
当线程启动start()方法后,进入”可执行”状态,java程序中执行用户写的run()方法。执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体——run()方法,这样就使得该线程处于可运行状态(Runnable)。这一状态并不是运行中状态(Running),因为线程也许实际上并未真正运行。
非可执行:
在可执行状态下,线程可能执行完毕,也可能没执行完,当线程离开可执行状态,线程就进入来非可执行状态,用户可以使用wait(),sleep()方法进入非可执行状态。
消亡:
当run()方法执行完毕后,线程自动消亡。

2.为什么要用多线程(why)

多线程是为了使得多个线程并行的工作已完成多项任务,以提高系统的效率。

线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。

3.如何使用多线程(how)

3.1线程的创建

线程的创建包括主线程的创建,实现Runnable接口和继承Thread类三种。后面内容主要学习继承Thread创建多线程。
3.1.1主线程的创建
通过调用currentThread()获得主线程的引用。
代码如下:

package com.xxm.javaLange.test;

//创建主线程

public class ThreadDemo1 {
    public static void main(String[] args){
        Thread t = Thread.currentThread();
        System.out.println("主线程"+t);
        t.setName("My Thread");
        System.out.println("改变名称后:"+t);

        try{
            for(int i = 0;i<5;i++){
                System.out.println(i);
                Thread.sleep(2000);
            }
        }
        catch(Exception e){
            System.out.println("发生异常");
        }
    }
}

结果:

主线程Thread[main,5,main]
改变名称后:Thread[My Thread,5,main]
0
1
2
3
4

3.1.2实现Runnable接口创建线程
代码如下:

package com.xxm.javaLange.test;

//用Runnable接口实现线程

public class ThreadDemo2 {
    public static void main(String[] args){
        MyThread r = new MyThread();
        Thread t = new Thread(r);

        t.start();
        //主程序内容
        for(int i=0;i<5;i++){
            System.out.println(i);
        }
    }
}


//通过实现Runnable接口创建线程
class MyThread implements Runnable{
    int i=0;

    public void run(){//这个run()相当于用this指向了本MyThread实例,所以可以不用Thread.CurrenThread来调用。
        while(true){
            System.out.println("Aa"+i++);
            if(i==3){
                break;
            }
        }
    }
}

结果:

0
Aa0
1
2
3
Aa1
Aa2
4

3.1.3 继承Thread类创建线程
代码:

package com.xxm.javaLange.test;

//继承Thread类创建线程

public class ThreadDemo3 {
    public static void main(String[] args){
        MyThreadOne r = new MyThreadOne();
        r.start();
        for(int i=0;i<5;i++){
            System.out.println("b");
        }
    }
}

class MyThreadOne extends Thread{
    int i;
    public void run(){
        while(true){
            System.out.println("a"+i++);
            if(i==3){
                break;
            }
        }
    }
}

结果:

b
b
b
b
b
a0
a1
a2

对比实现Runnable接口和继承Thread类创建线程。因为Java是单继承的,当继承了Thread类后,该类酒不能同时继承其他类了,只能使用Runnable接口来实现。

3.2多线程的创建

用继承Thread类创建多线程
代码如下:

package com.xxm.javaLange.test;

//用继承Thread类创建多线程

public class ThreadDemo4 {
    public static void main(String[] args){
        MyThread1 mt1 = new MyThread1("first");
        MyThread2 mt2 = new MyThread2("second");
        MyThread3 mt3 = new MyThread3("third");
        mt1.start();
        mt2.start();
        mt3.start();
        int i = 20;
        while(true){
            System.out.println("main");
            i--;
            if(i == 0){
                break;
            }


        }

    }

}

class MyThread1 extends Thread{
    String name ;
    MyThread1(String threadname){
        name = threadname;
    }
    public void run(){
        try{
            for(int i=0;i<5;i++){
                System.out.println(name + ":" + i);
                //Thread.sleep(1000);
            }
        }
        catch(Exception e){
            System.out.println(e);
        }
        System.out.println(name+"退出");
    }
}

class MyThread2 extends Thread{
    String name ;
    MyThread2(String threadname){
        name = threadname;
    }
    public void run(){
        try{
            for(int i=0;i<5;i++){
                System.out.println(name + ":" + i);
                //Thread.sleep(1000);
            }
        }
        catch(Exception e){
            System.out.println(e);
        }
        System.out.println(name+"退出");
    }
}

class MyThread3 extends Thread{
    String name ;
    MyThread3(String threadname){
        name = threadname;
    }
    public void run(){
        try{
            for(int i=0;i<5;i++){
                System.out.println(name + ":" + i);
                //Thread.sleep(1000);
            }
        }
        catch(Exception e){
            System.out.println(e);
        }
        System.out.println(name+"退出");
    }
}

结果:

third:0
third:1
third:2
third:3
third:4
main
main
main
main
first:0
first:1
first:2
first:3
first:4
first退出
second:0
second:1
second:2
second:3
second:4
second退出
third退出
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main

3.4线程的调度

线程调度是由JVM完成。由它来分配cpu时间段给线程,而先分配给哪个线程,由线程的优先级决定。
3.4.1线程的优先级
理论上获得线程优先级高的,可以获得更多的CPU时间,但实际上,获得CPU时间长短由多个因素决定,不能只看优先级。用户可以使用setPriority()方法来设置线程优先级。该方法声明如下:

Final void setPriority(int level)

参数level用来设置优先级大小。优先级大小范围为1-10。1表示最小,可直接用常量MIN_PRIORITY表示。10表示最大,可用常量MAX_PRIORITY表示。
获取当前优先级,用户可用getPriority()方法,声明如下:

Final getPriotity()

下面看代码例子:

这里写代码片

3.4.2 join() 方法
join()方法能使当前的线程停下来等待,直到调用join()方法的线程运行结束,当前线程才继续运行。
join()方法遵循:谁调用谁等待。下面用代码说明

package com.xxm.javaLange.test;

class Sleeper extends Thread{
    private int sleeptime;
    public Sleeper(String name,int sleepTime){
        super(name);
        sleeptime = sleepTime;
        start();

    }


public void run(){
    try{
        for(int i=0;i<5;i++){
            System.out.println(getName()+"打印 "+i);
            sleep(sleeptime);
        }
    }
    catch(Exception e){
        System.out.println(getName()+" interrupted");
    }
    System.out.println(getName()+"执行完毕");
}
}

class Joiner extends Thread{
    private Sleeper sleeper;
    public Joiner(String name,Sleeper sleeper){
        super(name);
        this.sleeper = sleeper;
        start();
    }

    public void run(){
        try{
            System.out.println(getName()+"开始执行");
            System.out.println(sleeper.getName()+"开始使用join()方法");
            sleeper.join();
            for(int i=5;i>0;i--){
                System.out.println(getName()+"打印"+i);
            }

        }
        catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(getName()+"执行完毕");
    }
}

public class ThreadDemo5 {
    public static  void main(String[] args){
        Sleeper sleeper = new Sleeper("线程A",1500);          
        new Joiner("线程B",sleeper);

    }
}

结果:

线程A打印 0
线程B开始执行
线程A开始使用join()方法
线程A打印 1
线程A打印 2
线程A打印 3
线程A打印 4
线程A执行完毕
线程B打印5
线程B打印4
线程B打印3
线程B打印2
线程B打印1
线程B执行完毕

从程序看出。线程B调用了线程A的join()方法。根据谁调用谁等待的原测。线程B要等待线程A执行完成后,才能再执行。
当然join()方法可以加入参数,决定等待的时长。如:join(1000)表示等待1s。
3.4.3 sleep()方法
sleep()方法又叫做睡眠方法,是让当前线程停止一段时间后再执行,时间单位为毫米,下面用例子说明:

package com.xxm.javaLange.test;

//sleep()方法

class sleepTest extends Thread {
    int count = 0;

    public sleepTest(){
        start();
    }


    public void run(){
        int count = 3;
        while(true){
            System.out.println(this);
            try{
                sleep(1000);//停1秒

            }
            catch(Exception e){
                System.out.println("i am interrupted");
            }
            count--;
            if(count == 0)return;
        }
    }
}

public class ThreadDemo6{

public static void main(String[] args){
    for(int i=0;i<3;i++){
        new sleepTest();
    }
    new sleepTest();
}
}

结果:

Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-2,5,main]
Thread[Thread-3,5,main]
Thread[Thread-1,5,main]
Thread[Thread-3,5,main]
Thread[Thread-2,5,main]
Thread[Thread-0,5,main]
Thread[Thread-3,5,main]
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]
Thread[Thread-2,5,main]

结果中每打印四行就会停1s,直到打印完。
3.4.4 yield()方法
yield()方法又叫做让步方法,作用时让有相同优先级的线程获得运行时间,代码如下:

package com.xxm.javaLange.test;

//yield()方法

public class ThreadDemo7 {
    public static void main(String[] args){
        MyThread4 mt1 = new MyThread4();
        MyThread5 mt2 = new MyThread5();
        MyThread6 mt3 = new MyThread6();
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

class MyThread4 extends Thread{
    public void run(){
        for(int i=0;i<20;i++){
            System.out.print("@");
            Thread.yield();
        }
    }
}

class MyThread5 extends Thread{
    public void run(){
        for(int i=0;i<20;i++){
            System.out.print("!");
            Thread.yield();
        }
    }
}

class MyThread6 extends Thread{
    public void run(){
        for(int i=0;i<20;i++){
            System.out.print("#");
        }
    }
}

结果:

@####################!@!@!@!@!@!@!@!@!@!@@!@!@@!@!@@!@!@!!!!

在打印@和!时候使用了yield()方法。所以他们会一起同时获得运行机会。所以@和!会交错打印,#会单独打印。

3.5多线程同步

3.5.1 同步的必要性
这也是说明为什么(why)要有同步机制。
举个例子,某电商网站有一个商品,数量共10件,用户A的购物车里收藏了该商品6件,用户B购物车里收藏了该商品8件。如果2人同时下单购买该商品,很明显,他们2个都不能买到商品。所以必须要让他们排队购买,这就要引入同步机制了。
3.5.2 实现同步(how)
使用关键字 synchronized 。
代码如下:

package com.xxm.javaLange.test;

//实现同步

public class ThreadDemo9 extends Thread{
private String cha;
static Object printer = new Object();
public ThreadDemo9(String cha){
    this.cha = cha;
}
void print(){
    synchronized(printer){
        for(int i= 0;i<2;i++)
            System.out.print(cha+" ");
    }
}
public void run(){
    for(int i = 1;i<3;i++){
        print();
        System.out.println();
    }
}
public static void main(String[] args){
    ThreadDemo9 test1 = new ThreadDemo9("线程A");
    ThreadDemo9 test2 = new ThreadDemo9("线程B");
    ThreadDemo9 test3 = new ThreadDemo9("线程C");
    test1.start();
    test2.start();
    test3.start();
}
}

结果:

线程A 线程A 
线程C 线程C 
线程B 线程B 
线程C 线程C 
线程A 线程A 
线程B 线程B 

3.5.3 死锁
怎样产生死锁。就是,线程A在等待线程B,线程B也在等待线程A。如何线程A和线程B一直等待下去。变成了死锁。
代码如下:

package com.xxm.javaLange.test;

//死锁

public class ThreadDemo10 {
    public static void main(String[] args){
        String s1 = "s1";
        String s2 = "s2";
        MyThread7 mt1 = new MyThread7(s1,s2,"NO!");
        MyThread7 mt2 = new MyThread7(s2,s1,"NO!");
        mt1.start();
        mt2.start();
    }
}

class MyThread7 extends Thread{
    String s1,s2;
    public MyThread7(String s1,String s2,String name){
        this.s1 = s1;
        this.s2 = s2;
        this.setName(name);
    }
    public void run(){
        synchronized(s1){
            System.out.println(this.getName()+"锁住"+s1);
            try{
                Thread.sleep(10);;
            }
            catch(Exception e){
                e.printStackTrace();
            }
            synchronized(s2){
                System.out.println(this.getName()+"锁住"+s2);
                System.out.println("线程结束");
            }
        }
    }
}

结果:

NO!锁住s1
NO!锁住s2

程序中synchronized里面又嵌套来一个synchronized,所以处于死锁状态下,运行打印完上2句后,程序一直运行在等待状态。

3.6线程间通信

这个要另开一篇了。见JAVA复习(7)

参考资料:
《Java核心开发技术从入门到精通》-第15课
http://www.cnblogs.com/mengdd/archive/2013/02/16/2913649.html
http://blog.csdn.net/mayouarebest8621/article/details/6755036
http://www.cnblogs.com/haitao-fan/archive/2012/05/31/2528398.html


XianMing

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值