多线程常用基础

一:进程与线程的描述:

进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含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

Thread和Runnable的区别:

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

问题解决

原文地址:http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html

 
时间: 2024-11-10 12:01:10

多线程常用基础的相关文章

.NET基础拾遗(5)多线程开发基础

Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 一.多线程编程的基本概念 下面的一些基本概念可能和.NET的联系并不大,但对于掌握.NET中的多线程开发来说却十分重要.我们在开始尝试多线程开发前,应该对这些基础知识有所掌握,并且能够在操作系统层面理解多线程的运行方式. 1.1 操作系统层面的进程和线程 (1)进程 进程代表了操作系统上运行着的一个应用程序.进程拥有自己的程序块

多线程编程基础知识

多线程编程基础知识 http://www.cnblogs.com/cy163/archive/2006/11/02/547428.html 当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义.现在的大型应用软件无一不是多线程多任务处理,单线程的软件是不可想象的.因此掌握

R语言常用基础知识

seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)),    length.out = NULL, along.with = NULL, ...) 举例----------Examples----------seq(0, 1, length.out=11) seq(stats::rnorm(20)) #  seq(1, 9, by = 2)     #  seq(1, 9, by = pi)    #  seq(1, 6, by =

多线程常用的一些知识点

多线程常用的一些知识点: /**  * @author Rollen-Holt  * 取得线程的名称  * */ class hello implements Runnable {     public void run() {         for (int i = 0; i < 3; i++) {             System.out.println(Thread.currentThread().getName());//说明如果我们没有指定名字的话,系统自动提供名字.      

Java多线程编程基础之线程对象

在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念. [线程的并发与并行] 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent).而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel). 在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储

多线程的基础知识

多线程编程基础知识 当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义.现在的大型应用软件无一不是多线程多任务处理,单线程的软件是不可想象的.因此掌握多线程多任务设计方法对每个程序员都是必需要掌握的.本实例针对多线程技术在应用中经常遇到的问题,如线程间的通信.同步等,分

Linux系统常用基础命令

Linux系统常用基础命令 cd->切换目录: pwd->显示当前所在的绝对目录; chmod->用于改变linux系统文件或目录的访问权限; ls->不仅可以查看linux文件夹包含的文件,而且可以查看文件权限(包括目录.文件夹.文件权限)查看目录信息等等; mkdir->创建文件夹; rm->删除一个目录中的一个或多个文件或目录; rmdir->从一个目录中删除一个或多个子目录项,删除某目录时也必须具有对其父目录的写权限;注意:不能删除非空目录; mv-&g

多线程(基础篇1)

在多线程这一系列文章中,我们将讲述C#语言中多线程的相关知识,在多线程(基础篇)中我们将学习以下知识点: 创建线程 中止线程 线程等待 终止线程 确定线程的状态 线程优先级 前台线程和后台线程 向线程传递参数 使用C#的lock关键字锁定线程 使用Monitor锁定线程 处理异常 一.创建线程 在整个系列文章中,我们主要使用Visual Studio 2015作为线程编程的主要工具.在C#语言中创建.使用线程只需要按以下步骤编写即可: 1.启动Visual Studio 2016,新建一个控制台

iOS常用基础框架和第三方类库

iOS 常用基础框架 框架名称 功能 Foundation 提供OC的基础类(像NSObject).基本数据类型等 UIKit 创建和管理应用程序的用户界面 QuartzCore 提供动画特效以及通过硬件进行渲染的能力 CoreGraphics 提供2D绘制的基于C的API SystemConfiguration 检测当前网络是否可用和硬件设备状态 AVFoundation 提供音频录制和回放的底层API,同时也负责管理音频硬件 CFNetwork 访问和配置网络,像HTTP.FTP和Bonjo