首页 > 代码库 > 多线程常用基础

多线程常用基础

一:进程与线程的描述:

       进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程。(进程是资源分配的最小单位)

  线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

技术分享

二:实现多线程的常用方式:

    1.继承Thread类
    2.实现Runnable接口

三:对于直接继承Thread

        看例子:

 

public class Zy  extends Thread{

    private  String name;
    public Zy(){}

    public  Zy(String name){
        this.name = name;
    }
    public  void run(){

        for (int i=0;i<5;i++){
            System.out.println(name+"执行     "+i+"次");
        }
    }

 

 

 

public class Test {

    public static void main(String[] args) {

             Zy z1 = new Zy("z1");
             Zy z2 = new Zy("z2");
             z1.run();    //这里不能直接调用test()方法
             z2.run();    //这里的run方法手是重写Thread的run方法,然后调用才能start()
    }

}

 

 

       实现效果:

z1执行     0次
z1执行     1次
z1执行     2次
z1执行     3次
z1执行     4次
z2执行     0次
z2执行     1次
z2执行     2次
z2执行     3次
z2执行     4次

 

这种方法执行顺序是不对的,应该调用start()方法

 public static void main(String[] args) {

             Zy z1 = new Zy("z1");
             Zy z2 = new Zy("z2");
             z1.start();
             z2.start();

    }

 

注:只有重写了Thread里的run()方法start()方法才能实现效果。

实现效果

z2执行     0次
z1执行     0次
z2执行     1次
z1执行     1次
z2执行     2次
z1执行     2次
z2执行     3次
z1执行     3次
z2执行     4次
z1执行     4次

因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的。

至于为什么不能直接调用run()方法,应该是线程的运行需要本地操作系统的支持。

此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。

 

通过实现Runnable接口

public class Zy implements Runnable{

    private  String name;
    public Zy(){}

    public  Zy(String name){
        this.name = name;
    }
    public  void run(){

        for (int i=0;i<5;i++){
            System.out.println(name+"执行     "+i+"次");
        }
    }


}

  

  public static void main(String[] args) {

             Zy z1 = new Zy("z1");
             Thread t1 = new Thread(z1);
             Zy z2 = new Zy("z2");
             Thread t2 = new Thread(z2);
             t1.start();
             t2.start();

    }

 //执行结果

第一次:
z1执行 0次 z1执行 1次 z2执行 0次 z2执行 1次 z2执行 2次 z2执行 3次 z2执行 4次 z1执行 2次 z1执行 3次 z1执行 4次
第二次:

z1执行 0次
z1执行 1次
z1执行 2次
z1执行 3次
z1执行 4次
z2执行 0次
z2执行 1次
z2执行 2次
z2执行 3次
z2执行 4次

 //每次执行结果大致都不一样,如果大致一样可能你的操作系统比较稳定,呵呵

 

 关于选择继承Thread还是实现Runnable接口?

   其实查看源码发现

    

public
class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

 Thread也是实现了Runnable接口,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式 我不太清楚  不过这:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.html

ThreadRunnable的区别:

 

1.如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

  这里实现继承Thread

public class Zy extends Thread{
private  int count=5;
public void run(){

for (int i = 0; i < 6; i++) {
if (count > 0) {
System.out.println("数量= " + count--);
}
}

}
public static void main(String[] args) {

Zy z1 = new Zy();
Thread t1 = new Thread(z1);
Zy z2 = new Zy();
Thread t2 = new Thread(z2);
t1.start();
t2.start();

}
 }

 

执行效果

数量= 5
数量= 4
数量= 3
数量= 2
数量= 1
数量= 5
数量= 4
数量= 3
数量= 2
数量= 1

 这种说明资源并不能共享,换成runnable接口

 

public class Zy implements Runnable{

    private  int count=5;
    public  void run(){

        for (int i = 0; i < 6; i++) {
            if (count > 0) {
                System.out.println("数量= " + count--);
            }
        }

    }
    public static void main(String[] args) {

         Zy zy = new Zy();
         new Thread(zy,"1号窗口").start();
         new Thread(zy, "2号窗口").start();
         new Thread(zy, "3号窗口").start();

    }


}

 (可能实现的)实现效果

数量= 5
数量= 2
数量= 1
数量= 3
数量= 4

  

总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

另外:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM事实上就是在操作系统中启动了一个进程。

 

 四:判断线程是否启动

public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Zy zy = new Zy();
        Thread demo = new Thread(zy);
        System.out.println("线程启动之前---》" + demo.isAlive());
        demo.start();
        System.out.println("线程启动之后---》" + demo.isAlive());
    }

  执行结果

线程启动之前---》false
线程启动之后---》true
Thread-0
Thread-0
Thread-0

  

主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。

 

五:线程的强制执行join:

    

public class Zy implements Runnable{

    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Zy zy = new Zy();
        Thread demo = new Thread(zy,"线程");
        demo.start();
        for(int i=0;i<10;++i){
            if(i>5){
                try{
                    demo.join();  //强制执行demo
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main 线程执行-->"+i);
        }
    }


}

  

 执行结果

main 线程执行-->0
main 线程执行-->1
main 线程执行-->2
main 线程执行-->3
main 线程执行-->4
main 线程执行-->5
线程
线程                    //就是在主线程没有结束之前强行执行
线程
main 线程执行-->6
main 线程执行-->7
main 线程执行-->8
main 线程执行-->9

 六:线程休眠sleep:

public class Zy implements Runnable{

    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(2000);    //2000指2秒
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }

    public static void main(String[] args) {
        Zy zy = new Zy();
        Thread demo = new Thread(zy, "线程");
        demo.start();
    }


}

 

 执行结果

线程0 
线程1
线程2   //没两秒打印一个

 

    七,线程中断interrupt()

    

public class Zy implements Runnable{

    public void run() {
        System.out.println("执行run方法");
        try {
            Thread.sleep(10000);
            System.out.println("线程完成休眠");
        } catch (Exception e) {
            System.out.println("休眠被打断");
            return;  //返回到程序的调用处
        }
        System.out.println("线程正常终止");
    }

    public static void main(String[] args) {
        Zy zy = new Zy();
        Thread demo = new Thread(zy, "线程");
        demo.start();
        try{
            Thread.sleep(2000);  
        }catch (Exception e) {
            e.printStackTrace();
        }
        demo.interrupt(); //2s后中断线程
    }
}

 

 执行结果

执行run方法

-----间隔两秒后

休眠被打断

 

java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程消失了,此后台线程依然能够继续运行。主线程中断 不会打断子线程

 

public class Zy implements Runnable{

    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "在运行");
        }
    }

    public static void main(String[] args) {
        Zy zy = new Zy();
        Thread demo = new Thread(zy, "线程");
        demo.setDaemon(true);
        demo.start();
    }


}

 

八:线程的优先级

      查看Thread源码发现

 /**
     * The minimum priority that a thread can have.  //最低优先级
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    //MIN_PRIORITY值越小优先级越低

 

public class Zy implements Runnable{

    public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"运行"+i);
        }
    }

    public static void main(String[] args) {
        Thread h1=new Thread(new Zy(),"A");
        Thread h2=new Thread(new Zy(),"B");
        Thread h3=new Thread(new Zy(),"C");
        h1.setPriority(8);
        h2.setPriority(2);
        h3.setPriority(6);
        h1.start();
        h2.start();
        h3.start();
    }


}

 

打印效果

A运行0
A运行1
A运行2
A运行3
A运行4
C运行0
C运行1
C运行2
C运行3
C运行4
B运行0
B运行1
B运行2
B运行3
B运行4

 

 

但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、不知道是不是

NORM_PRIORITY值越高,谁就可以先拿到cpu资源,我执行了n次结果都是一样的?

另外,主线程的优先级是5.

九:线程的礼让。

    在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。

public class Zy implements Runnable{

    public void run() {
        for(int i=0;i<5;++i) {
            System.out.println(Thread.currentThread().getName() + "运行" + i);
            if (i == 3) {
                System.out.println("线程的礼让");
                Thread.currentThread().yield();
            }
        }
    }

    public static void main(String[] args) {
        Thread h1=new Thread(new Zy(),"A");
        Thread h2=new Thread(new Zy(),"B");
        h1.start();
        h2.start();
    }


}

 

执行效果

A运行0
A运行1
A运行2
A运行3
线程的礼让
B运行0
B运行1
B运行2
B运行3
线程的礼让
A运行4
B运行4

 

 

十:同步和死锁。

      

public class Zy implements Runnable{
    private int count=5;
    public void run() {
        for(int i=0;i<10;++i){
            if(count>0){
                try{
                    Thread.sleep(1000);  //休息一秒
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(count--);
           }
        }
    }

    public static void main(String[] args) {
       Zy zy = new Zy();
        Thread h1=new Thread(zy);
        Thread h2=new Thread(zy);
        Thread h3=new Thread(zy);
        h1.start();
        h2.start();
        h3.start();
    }


}

 

执行效果

5
3
4
2
2
2
1
0
-1

 

这里应该是线程之间出现了纷争,

如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

其他的线程必须等到这个线程结束之后才能继续执行。

【同步代码块】:

语法格式:

synchronized(同步对象){

 //需要同步的代码

}

public class Zy implements Runnable{
    private int count=5;
    public void run() {
        for(int i=0;i<10;++i){
            synchronized (this) {
                if (count > 0) {
                    try {
                        Thread.sleep(1000);  //休息一秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(count--);
                }
            }
        }
    }

    public static void main(String[] args) {
       Zy zy = new Zy();
        Thread h1=new Thread(zy);
        Thread h2=new Thread(zy);
        Thread h3=new Thread(zy);
        h1.start();
        h2.start();
        h3.start();
    }


}

 

执行结果

5
4
3
2
1
//没秒输出一个

 

 

【同步方法】

也可以采用同步方法。

语法格式为synchronized 方法返回类型方法名(参数列表){

    // 其他代码

}

public class Zy implements Runnable{
    private int count=5;
    public void run() {
        for (int i = 0; i < 10; ++i) {
            sale();
        }
    }
    public synchronized void sale() {
        if (count > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(count--);
        }
    }

    public static void main(String[] args) {
       Zy zy = new Zy();
        Thread h1=new Thread(zy);
        Thread h2=new Thread(zy);
        Thread h3=new Thread(zy);
        h1.start();
        h2.start();
        h3.start();
    }


}

 

//输出结果一样,不信 你可以试一下

 

死锁

当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

【生产者和消费者问题】

/*
*公共信息类
*/
public class Info {
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private String name = "Rollen";
    private int age = 20;
}

/**
*消费者类
*/
public class Consumer implements Runnable {
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }

    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.info.getName()+"<---->"+this.info.getAge());
        }
    }
}
/**
*生产类
*/
public class Producer implements Runnable {
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }

    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                this.info.setName("Rollen");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(20);
                flag=false;
            }else{
                this.info.setName("chunGe");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(100);
                flag=true;
            }
        }
    }

}

 

测试类

public static void main(String[] args) {

        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();

    }

 

 

测试结果

Rollen<---->100
chunGe<---->20
chunGe<---->100
Rollen<---->100
chunGe<---->20
chunGe<---->20
Rollen<---->100
Rollen<---->100
Rollen<---->100
chunGe<---->20
chunGe<---->100
chunGe<---->20
chunGe<---->20
chunGe<---->20
chunGe<---->100
Rollen<---->20
chunGe<---->100
Rollen<---->20
chunGe<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
Rollen<---->20
chunGe<---->20   //名字和年龄并不对应

 

那么如何解决呢?

1)加入同步

2)加入等待和唤醒

首先是加入同步

class Info {
     
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public synchronized void set(String name, int age){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
    }
     
    public synchronized void get(){
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
    }
    private String name = "Rollen";
    private int age = 20;
}
 
/**
 * 生产者
 * */
class Producer implements Runnable {
    private Info info = null;
 
    Producer(Info info) {
        this.info = info;
    }
 
    public void run() {
        boolean flag = false;
        for (int i = 0; i < 25; ++i) {
            if (flag) {
                 
                this.info.set("Rollen", 20);
                flag = false;
            } else {
                this.info.set("ChunGe", 100);
                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 < 25; ++i) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}
 
/**
 * 测试类
 * */
class hello {
    public static void main(String[] args) {
        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

 

实现效果

Rollen<===>20
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100

 

名字和年龄是对应了,但是存在有重复覆盖的问题

如果想解决这个问题,就需要使用Object类帮忙了、

,我们可以使用其中的等待和唤醒操作。

要完成上面的功能,我们只需要修改Info类饥渴,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下

public synchronized void set(String name, int age){
        if(!flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
        flag=false;
        super.notify();
    }
     
    public synchronized void get(){
        if(flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
         
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
        flag=true;
        super.notify();
    }
    private String name = "Rollen";
    private int age = 20;
    private boolean flag=false;

 

 

结果

Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20

 

问题解决

 

 

 

 

 

 

 

 

 

 

 

多线程常用基础