黑马程序员——多线程

——- android培训java培训、期待与您交流! ———-

线程和进程是什么?:

进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。
该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。

一个进程中至少有一个线程。
java虚拟机启动的时候会有一个进程java.exe。

该进程中至少有一个线程负责java程序执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为 主线程。

创建线程的第一种方式:继承Thread类

步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法。
该方法有两个作用:启动线程、调用run方法

代码示例:

class ThreadDemo extends Thread {//1,继承Thread类
    public void run() {//2,复写run方法
        for (int i = 0; i < 60; i++) {
            System.out.println("Thread----" + i);
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();// 创建好一个线程
        td.start(); // 3,开启线程并执行该线程的run方法
        // td.run //仅仅是普通的对象调用方法。线程虽然创建了,但是并没有运行
        for (int i = 0; i < 60; i++)
            System.out.println("hello------" + i);

    }
}

运行此程序发现:运行结果每一次都不同。

因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把,多线程的运行行为理解为互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,cpu说的算

td.start(); 和 td.run();的区别:
td.start();开启线程并执行该线程的run方法,相当于程序中有两个线程
一个线程执行main中的方法,一个线程执行run中的方法

td.run();就是普通的对象调用,并没有启动线程,相当于程序中只有一个
主线程在执行,当执行到td.dun()时,就调用该对象的方法,执行完后再回到
main方法中继续执行

注意:如果主函数创建两个线程在执行,那么栈中会创建两个run方法。
两个run方法中的内容都是独立的。
如:两个run方法中都有变量 num ,那么num在两个run方法中互不干扰。

/*
 * 练习:
 * 创建两个线程,和主线程交替运行。
 * 
 * 线程都有自己默认的名称。
 * 名称格式为Thread-编号, 该编号从0开始
 * 
 * Thread.currentThread(); 获取当前线程对象。
 * getName();   获取线程名称
 * 
 * 设置线程名称:setName或者调用线程的构造函数

 */
class ThreadDemo extends Thread{
    private String name;
    public ThreadDemo(String name){
        super(name);//调用Thread的构造函数,设置线程名称
    }
    public void run(){
        for(int i=0;i<60;i++){
            //this.getName获取线程名称,默认名称格式为 Thread-编号
            //this.getName可以改成Thread.currentThread().getName,因为Thread.currentThread()
            //可以获取线程的对象
            System.out.println(this.getName() + "..." + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        ThreadDemo td1 = new ThreadDemo("td1...哈哈");
        ThreadDemo td2 = new ThreadDemo("td2...呵呵");
        td1.start();
        td2.start();
        //td1.run();    //这种方式并不是创建两个线程并执行,只相当于方法调用
        //td2.run();    //这种方式并不是创建两个线程并执行,只相当于方法调用
        for(int i = 0;i<60;i++)
            System.out.println("main....嘿嘿");
    }
}

创建线程的第二种方式:实现Runnable接口

步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将要运行的代码存放在run方法中
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象,作为实际参数传递给Thread类的构造函数。
5.调用Thread类的start方法开启线程,使得start方法自动调用Runnable子类的的run方法。

实现方式(implements Runnable)和继承方式(extends Thread)有什么区别呢?(面试题)

继承Thread:线程代码存放在Thread的子类的run方法中。但是具备单继承的局限性
实现Runnable:线程代码存放在实现Runnable接口的子类的run方法中。避免了单集成的局限性

代码示例:

/*
 需求:简单的卖票程序。
 多个窗口同时卖票(假设为4个卖票窗口)
 */

class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法。注意:不是继承Thread
    private int i = 100;

    public void run() {//将要运行的代码存放在run方法中
        while (true) {
            if (i > 0)
                System.out.println(Thread.currentThread().getName() + "..."
                        + i--);
            else
                return;
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Ticket t = new Ticket();

        // 通过Thread类建立线程对象,并将Runnable接口的子类对象,
        //作为实际参数传递给Thread类的构造函数
        Thread th1 = new Thread(t);
        Thread th2 = new Thread(t); 
        Thread th3 = new Thread(t);
        Thread th4 = new Thread(t);

        th1.start();//调用Thread类的start方法开启线程
        th2.start();
        th3.start();
        th4.start();
    }
}

同步代码块:synchronized

synchronized(对象)
{
    需要被同步的代码块
}

同步代码快中的 对象 如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。(如同火车上的卫生间,当有人进去锁门后,外边的人只能等待)

同步的前提:(注意:如果加锁过后程序还是不安全,应该考虑程序中的锁是否符合这两个前提)
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,比较消耗资源。

代码示例:

/*
通过运行结果:发现出现了0,-1,-2等错票

多线程的运行出现了安全问题

问题的原因:
    当多条语句在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分,
    还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:
    对多条操作共享数据的语句,只能让一个线程执行完。在执行过程中,
    其他线程不可以参与执行。

java对于多线程的安全问题提供了专业的解决方式。

就是同步代码块:
*/
class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法
    private int i = 100;

    public void run() {
        while (true) {
            if (i > 0) {
                try {
                    Thread.sleep(10);// 使线程执行10后,等待10毫秒再继续执行
                } catch (InterruptedException e) {
                }
                System.out.println(Thread.currentThread().getName() + "..."+ i--);
            } else {
                return;
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        Thread th1 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th2 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th3 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th4 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        th1.start();
        th2.start();
        th3.start();
        th4.start();
    }
}

由于出现了错票,将以上代码修改为以下代码

class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法
    private int i = 100;
    Object obj = new Object();
    public void run() {
        while (true) {
            synchronized (obj) {//加上这句语句后,if代码没有执行完,其他的线程不许进入if语句
                if (i > 0) {
                    try {
                        Thread.sleep(10);// 使线程执行10后,等待10毫秒再继续执行
                    } catch (InterruptedException e) {
                    }
                    System.out.println(Thread.currentThread().getName() + "..."
                            + i--);
                } else {
                    return;
                }
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        Thread th1 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th2 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th3 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        Thread th4 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
        th1.start();
        th2.start();
        th3.start();
        th4.start();
    }
}
/* 
    需求:
    银行有一个金库。
    有两个用户分别存300元,每次存100元,存3次。

    目的:该线程是否有安全问题,如果有,如何解决

如何找到问题:
1,明确哪些代码是多线程运行代码。
    在本题中,多线程运行的代码是run方法和add方法
2,明确哪些是共享数据
    在本题中,对象j是共享数据,成员函数num也是共享数据。
3,明确多线程运行代码中,哪些语句是操作共享数据的
    在本题中,j.add是操作共享数据的,但是j.add只有一句话,不能被多线程给分开读。
    而num有两句话(num+=x,System.out.println(num)),是可以被多线程分开读的,
    如:第一个线程读到num+=x时,第二个线程插进来读取nun+=x,而导致第一个线程没有读到System.out.println(num)。
    所以,本题中要分析的就是num+=x,System.out.println(num)。


 */
class JinKu {   //金库类
    private int num = 0;
    Object obj = new Object();
    public void add(int x) {
        num += x;
//      try {Thread.sleep(10);} catch (InterruptedException e) {}//此处代码可以不用try,可以把异常抛出去。但是这样做在run方法中,必须处理异常
        System.out.println(num);
    }
//  该线程有问题,如果在该线程add方法中的num+=x下边,
//  加入try {Thread.sleep(10);} catch (InterruptedException e) {}
//  那么就会出现错误,应该将add()方法改为如下
//  public void add(int x){
//      synchronized (obj) {
//          num+=x;
//          try {Thread.sleep(10);} catch (InterruptedException e) {}
//          System.out.println(num);
//      }
//  }

}
class YongHu implements Runnable {  //用户类
    JinKu j = new JinKu();
    public void run() {
        for (int i = 0; i < 3; i++)
            j.add(100);
    }
}
public class Demo {
    public static void main(String[] args) {
        YongHu yh = new YongHu();
        Thread th1 = new Thread(yh);
        Thread th2 = new Thread(yh);
        th1.start();
        th2.start();
    }
}

同步函数和静态同步函数:

同步函数:public synchronized void show(){}
静态同步函数:public static synchronized void show(){}

同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数用的锁是this。
(记住结论就行:同步函数用的锁是this)

如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码对象。
类名.class 该对象的类型是class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
记住结论就行:静态同步函数用的锁是 类名.class

代码示例:

/*
 卖票的例子,使用同步函数来加锁
 */
class Test implements Runnable {
    int count = 100;

    public void run() {
        while (true) {
            maiPiao();
        }
    }

    public synchronized void maiPiao() {//在函数上加同步
        if (count > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
            System.out.println(Thread.currentThread().getName() + "..."
                    + count--);
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

死锁:

死锁就是,在我这个锁中用你那个锁,在你那个锁中又要用到我这个锁。

形象的比喻就是:两根筷子。A一根B一根,每人只拿一根筷子就无法吃饭。
除非B把筷子给A,或者A把筷子给B,这样才能吃饭。如果A不给B,B也不给A,
那么就无法吃饭,导致死锁

/*
    死锁程序(面试喜欢考)
 */
class Test1 implements Runnable {
    public void run() {
        while (true) {
            //使用Test1.class锁
            synchronized (Test1.class) {
                System.out.println("Test1...Test1.class");
                //使用Test2.class锁
                synchronized (Test2.class) {
                    System.out.println("Test1...Test2.class");
                }
            }
        }
    }
}

class Test2 implements Runnable {
    public void run() {
        while (true) {
            //使用Test2.class锁
            synchronized (Test2.class) {
                System.out.println("Test2...Test2.class");
                //使用Test1.class锁
                synchronized (Test1.class) {
                    System.out.println("Test2...Test1.class");
                }
            }
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        new Thread(new Test1()).start();
        new Thread(new Test2()).start();
        /*
        本示例中,发生死锁的原因:

            当Test1类中的程序输出"Test1...Test1.class"时,
            cpu突然切换到Test2类中的线程。
            于是就输出了"Test2...Test2.class"。

            这时,Test2类中的程序继续执行,读到了Test1.class锁,
            由于Test1.class锁在Test1类中没有释放,
            于是程序就在Test2类中等待Test1.class锁释放。

            这时,cpu切换到了Test1类中的程序,
            由于Test2.calss锁又在Test2类中序没有释放,
            于是Test1类中的程序在等待Test2.class释放。

            最终导致了,Test1类在等待Test2类释放锁,Test2类在等待Test1类释放锁,
            从而导致了死锁

        */
    }
}

多线程中的懒汉式

代码示例:

class Single{
    private static Single s = null;
    private Single(){}
    public static Single getSingle(){
        if (s == null) {//双重if判断语句
            synchronized (Single.class) {
                if (s == null) {
                    s = new Single();
                }
            }
        }
        return s;
    }
}

线程间的通讯:

线程间的通讯:可以理解为,一个线程在存数据,另一个线程在取数据。

如:
拉煤的例子:一个车在送煤,另外一个车在取煤。
生产者例子:生产者不断的生产物品,而消费者不断的消费物品

代码示例:

class Person {// 定义一个人类
    String name;
    String sex;// 性别
}

class Input implements Runnable {
    Person p;
    int x = 0;// 用来控制if和else语句,x为0时执行if,x为1时执行else

    public Input(Person p) {// 为了保证和Output类中的Person是同一个对象,将参数定义为Person,
                            // 这样就能确保传递进来的参数和Outpue类一样
        this.p = p;
    }

    public void run() {//此run方法不断的初始化姓名和性别。
        while (true) {
            synchronized (p) {// 此处的锁用p是因为,本类中的p和Output中的p是同一个对象,如果
                                // 此处不加同步输出结果不是我们想要的//
                                //可能会输出  “张三,nv”,“lili,男”等错误
                    if (x == 0) {
                        p.name = "张三";
                        p.sex = "男";
                    } else {
                        p.name = "lili";
                        p.sex = "nv";
                    }
                x = (x + 1) % 2;
            }
        }
    }
}

class Output implements Runnable {
    Person p;

    public Output(Person p) {// 为了保证和Input类中的Person是同一个对象,将参数定义为Person,
                                // 这样就能确保传递进来的参数和Input类一样
        this.p = p;
    }

    public void run() {//此run方法不断的获取姓名和性别
        while (true) {
            synchronized (p) {// 此处的锁用p是因为,本类中的p和Input中的p是同一个对象
                                // 此处不加同步输出结果会报错
                System.out.println(p.name + "...." + p.sex);
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Person p = new Person();

        Input in = new Input(p);
        Output out = new Output(p);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();
    }
}

发现,每次输出时候并不是存放一个数据输出一次,而是一下子输出一大片,
于是,将代码修改为下面的代码,让程序每存放一个数据,就输出一个数据。
使用等待唤醒机制。既:wait() 和 notify()
注意:wait()、notify()方法必须使用在同步代码块或者同步方法中

class Person {// 定义一个人类
    String name;
    String sex;// 性别
    boolean flag = false;   //标记是否已经存放了name和sex
}

class Input implements Runnable {
    Person p;
    int x = 0;// 用来控制if和else语句,x为0时执行if,x为1时执行else

    public Input(Person p) {// 为了保证和Output类中的Person是同一个对象,将参数定义为Person,
                            // 这样就能确保传递进来的参数和Outpue类一样
        this.p = p;
    }

    public void run() {
        while (true) {
            synchronized (p) {// 此处的锁用p是因为,本类中的p和Output中的p是同一个对象,如果
                                // 此处不加同步输出结果会报错
                if(p.flag)//如果存放了name和sex,那么就进入等待,当进入等待后,后边的语句都不执行
                    try {p.wait();} catch (InterruptedException e) {}//使用wait()时,必须标识对象(锁)
                if (x == 0) {
                    p.name = "张三";
                    p.sex = "男";
                } else {
                    p.name = "lili";
                    p.sex = "nv";
                }
                p.flag = true;  //存放name和sex后,让标记为true
                p.notify(); //在进入等待之前先唤醒Output中的wait(),使用notify()时,必须标识对象(锁)
                x = (x + 1) % 2;
            }
        }
    }
}

class Output implements Runnable {
    Person p;

    public Output(Person p) {// 为了保证和Input类中的Person是同一个对象,将参数定义为Person,
                                // 这样就能确保传递进来的参数和Input类一样
        this.p = p;
    }

    public void run() {
        while (true) {
            synchronized (p) {// 此处的锁用p是因为,本类中的p和Input中的p是同一个对象
                                // 此处不加同步输出结果会报错
                if(!p.flag) //如果已经输出了name和sex,那么就进入等待,进入等待后,后边的语句都不执行
                    try {p.wait();} catch (InterruptedException e) {}//使用wait()时,必须标识对象(锁)
                System.out.println(p.name + "...." + p.sex);
                p.flag = false; //输出name和sex后,让标记为false
                p.notify(); 在进入等待之前先唤醒Input中的wait(),使用notify()时,必须标识对象(锁)
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Person p = new Person();

        Input in = new Input(p);
        Output out = new Output(p);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();
    }
}
/* 
 * 练习:生产者和消费者
 * 要求:定义4个线程,生产者有两个,消费者有两个,当生产一个,必须被消费一个
 * 
 * 
 * 对于多个生产者和消费者。
 * 为什么要定义while判断标记。
 * 原因:让唤醒线程再判断一次标记
 * 
 * 为什么定义notifyAll。
 * 因为需要唤醒对方线程。
 * 因为只用notify容易出现只唤醒本方线程的情况。导致程序中所有线程都等待
 *
 * 但是呢notifyAll不仅唤醒了对方,而且还唤醒了本方
 */
class Test{
    int i = 0;
    boolean flag = false;
    // t1   t2
    public synchronized void shengChan(String name){
        while(flag)//必须用while循环,否则运行结果不是想要的
            try {wait();} catch (InterruptedException e) {}  // 【t1(放弃执行资格)   t2(放弃执行资格,同时唤醒t1)  t1(由于循环判断,放弃执行资格)】

        System.out.println(Thread.currentThread().getName()+"..."+name+"..."+i);
        flag = true;
        notifyAll();//必须全部唤醒,否则会出现所有线程都等待
    }

    // t3  t4
    public synchronized void xiaoFei(String name){
        while(!flag)//必须用while循环,否则运行结果不是想要的
            try {wait();} catch (InterruptedException e) {}// 【t3(放弃执行资格,同时唤醒t1,t2)  t4(放弃执行资格,同时唤醒t3) t3(由于循环判断,放弃执行资格)】
        System.out.println(Thread.currentThread().getName()+"..."+name+"......."+i++);
        flag = false;
        notifyAll();//必须全部唤醒,否则会出现所有线程都等待
    }
}
class ShengChan implements Runnable{
    Test t;
    public ShengChan(Test t){
        this.t = t;
    }
    public void run(){//此run方法不断的生产数据
        while(true){
            t.shengChan("生产者");
        }
    }
}
class XiaoFei implements Runnable{
    Test t;
    public XiaoFei(Test t){
        this.t = t;
    }
    public void run(){//此run方法不断的获取数据
        while(true){
            t.xiaoFei("消费者");
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Test t = new Test();

        ShengChan sc = new ShengChan(t);
        XiaoFei xf = new XiaoFei(t);

        Thread t1 = new Thread(sc);
        Thread t2 = new Thread(sc);
        Thread t3 = new Thread(xf);
        Thread t4 = new Thread(xf);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
} 

JDK 1.5中提供了多线程升级解决方案。Lock

将同步synchronized替换成了Lock操作。
将Object中的wait,notify,notifyAll,替换成了Condition对象。
该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。而上个示例的notifyAll不仅唤醒了对方,还唤醒了本方

代码示例:

import java.util.concurrent.locks.*;
class Test {
    int i = 0;
    boolean flag = false;
    private Lock lock = new ReentrantLock();// 创建一个锁,替代了synchronized
    private Condition condition_shengchan = lock.newCondition(); // 创建一个条件,替代了wait(),notify,notifyAll
    private Condition condition_xiaofei = lock.newCondition(); // 创建一个条件,替代了wait(),notify,notifyAll

    public void shengChan(String name) throws InterruptedException {//生产者
        lock.lock();// 相当于synchronized,只不过是替换了synchronized
        try {
            while (flag)// 必须用while循环,否则运行结果不是想要的   

                condition_shengchan.await();// 使生产者进入等待状态。使用该方法会抛出异常,所以在方法上声明异常,
                                            //该方法使生产这个锁进入等待
            System.out.println(Thread.currentThread().getName() + "..." + name
                    + "..." + i);
            flag = true;
            condition_xiaofei.signal();//在生产者进入等待之前,先唤醒消费,使得消费者开始执行
        } finally {
            lock.unlock();// 锁必须要被释放,必须要执行,所以放在finally中,就算出现异常,此处依旧执行
        }
    }

    public void xiaoFei(String name) throws InterruptedException {//消费者
        lock.lock();// 相当于synchronized,只不过是替换了synchronized
        try {
            while (!flag)// 必须用while循环,否则运行结果不是想要的

                condition_xiaofei.await();// 使消费者进入等待状态。使用该方法会抛出异常,所以在方法上声明异常。
                                            ////该方法使消费这个锁进入等待
            System.out.println(Thread.currentThread().getName() + "..." + name
                    + "......." + i++);
            flag = false;
            condition_shengchan.signal();//在消费者进入等待之前,先唤醒生产者,使得生产者开始执行
        } finally {
            lock.unlock();//锁必须要被释放,必须要执行,所以放在finally中,就算出现异常,此处依旧执行
        }
    }
}

class ShengChan implements Runnable {
    Test t;

    public ShengChan(Test t) {
        this.t = t;
    }

    public void run() {
        while (true) {
            try {
                t.shengChan("生产者");//由于该方法抛出异常,所以这里需要捕获异常
            } catch (InterruptedException e) {
            }
        }
    }
}

class XiaoFei implements Runnable {
    Test t;

    public XiaoFei(Test t) {
        this.t = t;
    }

    public void run() {
        while (true) {
            try {
                t.xiaoFei("消费者");//由于该方法抛出异常,所以这里需要捕获异常
            } catch (InterruptedException e) {
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Test t = new Test();

        ShengChan sc = new ShengChan(t);
        XiaoFei xf = new XiaoFei(t);

        Thread t1 = new Thread(sc);
        Thread t2 = new Thread(sc);
        Thread t3 = new Thread(xf);
        Thread t4 = new Thread(xf);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

interrupt

stop方法已经过时,那么如何停止线程?

只有一种方式,就是让run方法结束。
开启多线程运行,运行代码通常是循环结构。

只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:
当线程处于冻结状态。
就不会读取到标记。那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

Thread类提供该方法 interrupt();

import java.util.Scanner;
class Test implements Runnable{
    boolean flag = true;
    public synchronized void run(){
        while(flag){
            try {
                wait();//使用interrupt()方法,强制让线程抛出异常。在异常中将循环标记置为false,当重新回到循环判断时,由于flag为false,循环就结束了
            } catch (InterruptedException e) {
                flag = false;
            }
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(new Test());
        t.start();
        Scanner input = new Scanner(System.in);
        input.nextLine();//输入一个字符串
        t.interrupt();//当输入一个字符串后,直接中断t线程
    }
} 

setDaemon(boolean)

将线程标记为守护线程 setDaemon(boolean);

守护线程必须在线程开启前使用。既:在t.start()方法前使用
守护线程代表的是,当主线程结束时,守护线程也结束。它随着主线程存在而存在,随着主线程消失而消失

class Test implements Runnable{

    public void run(){
        while(true){
            System.out.println(Thread.currentThread().getName());
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Test());
        Thread t2 = new Thread(new Test());

        //标记为守护线程。在t.start()方法前使用
        t1.setDaemon(true);//true代表开启守护线程。
        t2.setDaemon(true);

        t1.start();
        t2.start();
        for(int i=0;i<50;i++){
            System.out.println("main...");
        }
    }
} 

join

join:
当A线程执行到了 B线程的.join方法时 ,A就会等待。等B线程执行完,A才会执行。
join可以用来临时加入线程执行

class ThreadDemo implements Runnable{
    public void run(){
        for(int i=0;i<30;i++){
            System.out.println(Thread.currentThread().getName()+"..."+i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        ThreadDemo st = new ThreadDemo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start(); 
        try {
            t1.join();//当主线程执行到此处时候,先等待t1线程执行完毕,主线程才继续往下执行
        } catch (InterruptedException e) {
        }
        t2.start();
        for(int i=0;i<30;i++){
            System.out.println("main........."+i);
        }
        System.out.println("over");
    }
}

setPriority

设置线程的优先级 setPriority(int num);
输出线程的主要信息 toString();

class ThreadDemo implements Runnable{
    public void run(){
        for(int i=0;i<30;i++){
            System.out.println(Thread.currentThread().toString());
            /*
             结果:
                Thread[Thread-0,10,main]
                Thread[Thread-1,5,main]

                Thread-0 或者Thread-1代表线程的编号。
                main代表该线程是被哪个线程开启的。很明显,此线程是被主线程开启的
                5代表优先级。优先级越高,获取到cpu执行权的概率越高。优先级最高为10
             */
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        ThreadDemo st = new ThreadDemo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();//主线程开启了一个线程

        t1.setPriority(10);//设置t1线程的优先级为10。那么t1线程抢到cpu执行权的概率就比较高

        t2.start();//主线程开启了一个线程
    }
}

Thread.yield()

释放执行权Thread.yield();
当线程执行到Thread.yield()时,线程会放弃执行权(既:cpu切换到该线程时,该线程不执行),然后再重新获取执行权。

例如:
当线程A放弃执行权后,线程B就更容易抢到cpu的执行权。
这样使得程序比较协调,使得每个线程被执行到的概率大些。既:A执行一次,B执行一次,A执行一次…..

class ThreadDemo implements Runnable{
    public void run(){
        for(int i=0;i<70;i++){
            System.out.println(Thread.currentThread().toString());
            Thread.yield();
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        ThreadDemo st = new ThreadDemo();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值