尚学堂--线程

本章内容:

  • 线程的基本概念
  • 线程的创建和启动
  • 线程的调度和优先级
  • 线程的状态控制
  • 线程同步

1、线程基本概念:

线程:一个程序里面不同的执行路径(main()方法叫主线程)。

进程:只是一个静态的概念,例如:机器上的一个.class文件,机器上的一个.exe文件这个叫一个进程

多线程: 在同一个时间点上,一个CPU只能支持一个线程在执行

程序执行过程:

 

  把程序的代码放到内存(代码区)里面,放进去之后并没开始执行,这个时候说明一个进程已经产生了,

 

但还没开始执行,这叫一个进程。进程是一个静态的概念,平时所说的进程的执行说的是进程里面主线程

 

(main方法)开始执行。进程是一个静态概念,实际上机器里面运行的都是线程。

CPU的执行是这样的:

  CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上,CPU只有一个线程在运行。什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。

 

 

2、线程的创建和启动

  • java的线程是通过java.lang.Thread类来实现的
  • VM启动时会有一个由主方法(public static void main() {} )所定义的线程
  • 可以通过创建Thread的实例来创建新的线程
  • 每个线程都是通过某个特定的Thread对象所对应的方法run()来完成操作的,方法run()称为线程体
  • 通过调用Thread类的start()方法来启动一个线程

创建一个新的线程:

  • 实现Runnable接口
  • 继承Thread类

实现Runnable接口:(Runnable接口只有一个run方法,重写run方法后,需要调用Thread中的start()方法就必须new一个Thread出来,并把线程类对象作为实参传入)

public class Test {
        public static void main(String[] args) {
                MyRunner mr = new MyRunner();//new一个线程类的对象
                //mr.run();  方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法
                Thread th = new Thread(mr);//要启动一个线程类就必须new一个Thread对象出来,调用Thread(Runnable target)构造方法
                th.start();//启动新开辟的线程,新线程执行的是run()方法,新线程与主线程并行执行

                for(int i = 0; i < 100; i ++) {
                        System.out.println("MainThread ------ " + i);
                }        
        }
}
//定义一个类来实现Runnable接口,实现Runnable接口就表示这个类是一个线程类
class MyRunner implements Runnable {
      public void run() {
        for(int i = 0; i < 100; i ++) {
                System.out.println("MyRunner : " + i);
        }
      }  
}

 继承Thread类:(Thread的子类要重写run方法,然后调用Thread的start方法启动新线程)

public class Test {
        public static void main(String[] args) {
                MyRunner mr = new MyRunner();
                mr.start();//调用start()方法启动新开辟的线程
                for(int i=0; i<=100; i++) {
                        System.out.println("MainThread ----" + i);
                }
        }
}
/*
MyRunner类从Thread类继承,通过实例化MyRunner的一个对象就能开辟一个新的线程,
调用从Thread类继承来的start()方法就可以启动新开辟的线程
*/
class MyRunner extends Thread {
        public void run() {
                for(int i=0; i<=100; i++) {
                        System.out.println("MyRunner : " + i);
                }
        }
}

  使用实现Runnable接口和继承Thread类这两种开辟新线程的方法的选择应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程

3、线程的调度和优先级

 

isAlive:就绪、运行、阻塞状态表示还“活”着,终止、new出来没有启动也是“死”的

4、线程的状态控制

sleep方法实例:

import java.util.*;//包中存放了Date()方法

public class Test {
    public static void main(String[] args) {
        MyRunner mr = new MyRunner();
        Thread tr = new Thread(mr);
        tr.start();//start自动调用run()方法开始新的进程
        
        try {
            Thread.sleep(10000);//sleep为静态方法,可以通过类直接调用,main进程暂停执行10s
        } catch(InterruptedException ae) {
            // no 
        }
        tr.interrupt();
    }
}

class MyRunner implements Runnable {
    public void run() {
        while(true) {
            System.out.println("====" + new Date() + "====");
            try {
                Thread.sleep(1000);//为什么用Thread.sleep,不能用MyRunner.sleep()
            } catch(InterruptedException ae) {
                return; //进程停止,不使用return,会一直输出时间,不停止
            }
            
        }
    }
}

john方法:

public class Test {
    public static void main(String[] args) {
        MyRunner mr = new MyRunner();
        Thread tr = new Thread(mr);
        tr.start();

        try {
            tr.join();//效果相当于方法调用
        } catch(InterruptedException ae) {
            //
        }

        for(int i=1; i<=10; i++) {
                System.out.println("i am main Thread");
            }    
    }
}

class MyRunner implements Runnable {
    public void run() {
        for(int i=0; i<=10; i++) {
            System.out.println("i am" + i);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException ae) {
                return;
            }
        }
    }
}

yield方法:

线程的优先级:

 

public class Test {
    public static void main(String[] args) {
        Thread t1 = new Thread(new T1());
        Thread t2 = new Thread(new T2());
        t1.setPriority(Thread.NORM_PRIORITY + 3);
        t1.start();
        t2.start();
        //因为是多线程,"t1的优先级:" + t1.getPriority() 语句不一定是在最后输出
        System.out.println("t1的优先级:" + t1.getPriority());    
    }
}

class T1 implements Runnable {
    public void run() {
        for(int i=0; i<=10; i++) {
            System.out.println("-----T1  " + i);
        }
    }
}

class T2 implements Runnable {
    public void run() {
        for(int i=0; i<=10; i++) {
            System.out.println("-----T2  " + i);
        }
    }
}

5、线程同步

 

public class Test implements Runnable {
    Timer test = new Timer();
    public static void main(String[] args) {
        Test t = new Test();//new线程类对象
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
    public void run() {
        test.add(Thread.currentThread().getName());
    }
}

class Timer {
    private static int num = 0;
    public void add(String name) {  
            num ++;
            try {
                Thread.sleep(1);//即使不写sleep,第一个线程执行过程中也很可能被第二个线程打断。Thread.sleep(1)只是为了放大这个效果
            } catch(InterruptedException e) {
                // no
            }
            System.out.println(name + ",你是第" + num + "个使用timer的线程");
    }
}
View Code

防止一个线程在执行过程中被另外的线程打断:
  • synchronized 声明方法
  • 将代码写在synchronized(this){}方法体中
public class Test implements Runnable {
    Timer test = new Timer();
    public static void main(String[] args) {
        Test t = new Test();//new线程类对象
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.setName("t1");//改变线程名称,使之与参数 name 相同
        t2.setName("t2");
        t1.start();//调用run()方法
        t2.start();
    }
    public void run() {
        test.add(Thread.currentThread().getName());
    }
}

class Timer {
    private static int num = 0;
    public synchronized void add(String name) {// 在声明方法时加入synchronized时表示在执行这个方法的过程之中当前对象被锁
        //synchronized(this) {  锁定当前对象(一个线程在执行方法体语句过程中不会被另外一个线程打断)
            num ++;
            try {
                Thread.sleep(1);//即使不写sleep,第一个线程执行过程中也很可能被第二个线程打断。Thread.sleep(1)只是为了放大这个效果
            } catch(InterruptedException e) {
                // no
            }
            System.out.println(name + ",你是第" + num + "个使用timer的线程");
        //}
    }
}

 线程死锁:

 

/*这个小程序模拟的是线程死锁的问题*/
public class TestDeadLock implements Runnable {
    public int flag = 1;
    static Object o1 = new Object(), o2 = new Object();

    public void run() {
        System.out.println(Thread.currentThread().getName() + "的flag=" + flag);
        /*
         * 运行程序后发现程序执行到这里打印出flag以后就再也不往下执行后面的if语句了 
         * 程序也就死在了这里,既不往下执行也不退出
         */

        /* 这是flag=1这个线程 */
        if (flag == 1) {
            synchronized (o1) {
                /* 使用synchronized关键字把对象01锁定了 */
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    /*
                     * 前面已经锁住了对象o1,只要再能锁住o2,那么就能执行打印出1的操作了 
                     * 可是这里无法锁定对象o2,因为在另外一个flag=0这个线程里面已经把对象o1给锁住了 
                     * 尽管锁住o2这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o2不放的
                     */
                    System.out.println("1");
                }
            }
        }
        /*
         * 这里的两个if语句都将无法执行,因为已经造成了线程死锁的问题 
         * flag=1这个线程在等待flag=0这个线程把对象o2的锁解开, 
         * 而flag=0这个线程也在等待flag=1这个线程把对象o1的锁解开 
         * 然而这两个线程都不愿意解开锁住的对象,所以就造成了线程死锁的问题
         */

        /* 这是flag=0这个线程 */
        if (flag == 0) {
            synchronized (o2) {
                /* 这里先使用synchronized锁住对象o2 */
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    /*
                     * 前面已经锁住了对象o2,只要再能锁住o1,那么就能执行打印出0的操作了 可是这里无法锁定对象o1,因为在另外一个flag=1这个线程里面已经把对象o1给锁住了 尽管锁住o1这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o1不放的
                     */
                    System.out.println("0");
                }
            }
        }
    }

    public static void main(String args[]) {
        TestDeadLock td1 = new TestDeadLock();
        TestDeadLock td2 = new TestDeadLock();
        td1.flag = 1;
        td2.flag = 0;
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.setName("线程td1");
        t2.setName("线程td2");
        t1.start();
        t2.start();
    }
}

 

posted @ 2017-07-25 23:22  亡命鸡礼花  阅读(137)  评论(0编辑  收藏  举报