黑马程序员------多线程

来源:互联网 发布:手机淘宝联盟怎么赚钱 编辑:程序博客网 时间:2024/06/04 00:52

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

概述
当程序要同时执行多条语句或者一些代码序需要执行费时的任务的时候可以使用多线程

自定义线程:
java创建线程是调用系统底层资源(由系统)进行创建,而不是Java虚拟机创建线程

创建线程的方法:
1、继承Thread类,成为Thread类的子类,并重写其中的run()方法
2、实现Runnable接口,并重写其中的run()方法,然后创建对象并作为参数存入Thread类对象中

**注意:**Windows 是多任务操作系统,Cpu在某一时间段只能执行一个程序(多核能执行多个),cpu是在程序之间不断地切换,由于切换速度快,人们误以为是同时执行

多线程特性:
随机性:谁抢到cpu执行权谁就执行

Thread类:
1、Thread类中定义了一个功能,用于存储线程要运行代码,该功能就是run()方法,run()方法中存储了要运行的代码
2、start()方法:开启线程并且执行run()方法中的代码
3、若是直接创建Thread对象并调用run()方法,此时代码并没有在子线程中运行,run()代码是在主线程中执行

线程创建—>结束示意图:
这里写图片描述

线程的方法(常用):
Thread.currentThread():当前线程
Thread.currentThread().toString():当前线程的信息,线程名称,优先级等
getName():获取线程名称
setName():设置线程名称
setPriority():设置线程优先级(并不是优先级最大就一直执行他,而是执行的获取cpu执行权的次数会多)
yield():暂停当前线程(释放执行权)

创建线程的三种方法:

public class Demo {    public static void main(String[] args) {    public void newThread() {"/** * 1、直接使用匿名内部类的方式创建线程,不要忘记调用start方法 * new Thread():创建一个线程,new RUnnable():创建匿名内部类对象,run():实现内部类方法 */"        new Thread(new Runnable() {            @Override            public void run() {                System.out.println(Thread.currentThread().toString());            }        }).start();"/** * 2、定义一个类继承Thread类          * public class Thread1 extends Thread {            public void run() {                    System.out.println(Thread.currentThread().toString());                }            }         */"        Thread1 t1 = new Thread1();        t1.run();"直接调用run方法只是调用方法并执行,但是不会再子线程中执行,而是在主线程中执行"        t1.start();"创建一个子线程并执行run()方法中的代码""/*** 3、创建一个类实现Runnable接口,实现Runnable接口并不是开启了线程,他只是实现了Thread类中的规则,使类能够作为参数传递给Thread,真正创建线程和执行线程的还是Thread类"          public class Runnable1 implements Runnable {                @Override                public void run() {                         System.out.println(Thread.currentThread().toString());                }            }        "创建一个Runnable对象"        Runnable r1 = new Runnable1();        "//创建一个线程,并把r1作为参数传入,指定执行r1中run方法中的代码"        Thread t = new Thread(r1);        t.start();    }}

注意:实现Runnable接口并不是开启了线程,他只是定义了run方法,创建线程和开启线程都是Thread类来执行,Runnable只是定义了规则
为什么要将Runnable接口子类对象传递给Thread类的构造函数:
因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定run方法,明确run方法所属地对象,否则Thread类会执行自己的run()方法

继承Thread类和实现Runnable接口的区别:
1、由于Java是单继承,一个类只能有一个父类,扩展性低
2、接口是可以多实现,扩展性高
3、继承Thread类:线程代码放在Thread类中run方法中
4、接口Runnable:线程代码放在接口子类的run方法中

线程安全问题:
原因:
当多条线程在同时操作共享数据时,并且有2条以上的操作语句,就会出现安全问题
(一个线程没有执行完,另一个线程中途获得执行权并开始执行,导致出现线程问题,但是操作语句只有一条的时候就不会产生安全问题)

解决方法:
1、同步:synchronized (obj) { }:
2、Lock

前提:
1、必须有两个以上的线程在操作共享数据
2、多个线程必须是同一个锁
(锁可以是任意对象,同步函数的锁是:this,静态同步函数的锁是:这个方法所在类的字节码文件 :类名.class)
原理:同一时间只能让一个线程在执行同步内的代码
范围:哪些代码操作了共享数据,这些代码就要同步(没有必要使用同步函数,范围太大,同时因为不断判断锁导致效率低)
1、明确哪些是多线程代码
2、明确共享数据
3、明确多线程运行代码中那些语句操作了共享数据

好处:解决了线程安全问题
坏处:降低了效率,当同步中嵌套同步时候会产生死锁
死锁:
原因:同步中嵌套同步

        Object a = new Object();        Object b = new Object();        "(1)"        synchronized (a) {            synchronized (b) {            }        }        "(2)"        synchronized (b) {            synchronized (a) {            }        }    }"死锁案例"public class AAA{    private Object obj1 = new Object();"//创建锁1"    private Object obj2 = new Object();"//创建锁2"    public void first() {        new Thread(new Runnable() {            @Override            public void run() {                while (true) {"//多次循环,执行一次不一定会有问题"                    synchronized (obj1) {"//获取锁1"                        System.out.println("obj1_first");                        synchronized (obj2) {"//获取锁2"                            System.out.println("obj2_first");                        }                    }                }            }        }).start();    }    public void second() {        new Thread(new Runnable() {            @Override            public void run() {                while (true) {                    synchronized (obj2) {"//获取锁2"                        System.out.println("obj2_second");                        synchronized (obj1) {"//获取锁1"                            System.out.println("obj1_second");                        }                    }                }            }        }).start();    }}public class Demo {    public static void main(String[] args) {        "/**         * 死锁案例         */"        AAA a = new AAA();        a.first();        a.second();    }}

(1)中先获取了锁a,现在要获取锁b,(2)中先获取了锁b,现在要获取锁a,这时候他们都不放开自己持有的锁,就会造成死锁

实例:

public class Thread1 {    private int ticket = 100;    public void sale1() {        new Thread(new Runnable() {            @Override            public void run() {                while (true) {                    "/*同步代码块,使用字节码文件作为锁,因为有两个以上的线程操作了共享数据*/                    不能把wile(true)放入同步中,因为这样会导致一个一个线程拿到锁之后一直执行,                    另外一个线程进不来"                    synchronized (Demo.class) {                        if (ticket > 0) {                            System.out.println(Thread.currentThread().toString() + "..." + ticket--);                        } else {                            break;                        }                    }                }            }        }).start();    }    public class AAA {    public static void main(String[] args) {        Thread1 t1 = new Thread1();        Thread1 t2 = new Thread1();        t1.start();        t2.start();    }}

线程间通信:
多个线程在操作同一个资源,但是他们的操作行为不同

等待唤醒机制:
同步: wait(),notify(),notifyall();

synchronized (a) {
“必须要用锁来调用这些方法,否则没有效果”
a.wait();”当前线程等待”
a.notify();”唤醒当前线程”
a.notifyall();”唤醒这个锁对应的所有线程”
}

为什么wait()等方法定义在Object中:锁可以是任意对象,所以要定义在obj中

Lock:
1、替代synchronized的方法
2、由于synchronized在唤醒时候会唤醒同一个锁的任意一个线程,不能对唤醒的对象进行控制

创建lock并获取监视器:
private Lock lock = new ReentrantLock(); 创建一个lock
private Condition con_in = lock.newCondition(); 获取监视器
private Condition con_out = lock.newCondition(); 获取监视器
con_in.await(); (等待) con_in.signal();(唤醒) con_in.signalAll();(唤醒全部)

实例:生产者和消费者

public class PruSale {    private String name;    private int num = 0;    private boolean flag = false;    private Lock lock = new ReentrantLock();"//创建一个锁"    private Condition con_set = lock.newCondition();"//创建一个监视器 con_set"    private Condition con_sale = lock.newCondition();"//创建一个监视器 con_sale"    public void set(String name) throws InterruptedException {        lock.lock();"//上锁,由于con_set只能在锁之中,所以放在此处"        "//不使用if是因为if只是判断一次,当线程唤醒后会从等待的地方继续执行,若是不进行判断会出错,导致多生产"        while (flag) {            con_set.await();"//这个线程使用con_set等待"        }        this.name = name;        num++;        System.out.println(Thread.currentThread().toString() + "name:" + name + ".num:" + num);        flag = true;"//更改flag"        con_sale.signal();"//唤醒使用con_sale等待的线程"        lock.unlock();    }    public void sale() throws InterruptedException {        "添加锁"        lock.lock();        while (!flag) {            "让线程等待"            con_sale.await();        }        System.out.println(Thread.currentThread().toString() + "name:" + name + "...num:...." + num);        flag = false;        "唤醒使用con_set.await()等待的线程"        con_set.signal();        lock.unlock();    }}public class In implements Runnable {    private PruSale p;    "使用构造方法来确定In和Out使用的是同一个对象"    public In(PruSale p) {        this.p = p;    }    @Override    public void run() {        while (true) {            try {                "调用方法"                p.set("zhangsan");            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}public class Out implements Runnable {    private PruSale p;    public Out(PruSale p) {        this.p = p;    }    @Override    public void run() {        while (true) {            try {                "调用方法"                p.sale();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}public class Demo {    private static int ticket = 100;    public static void main(String[] args) {        "/**         * 生产者消费者         */"        PruSale p = new PruSale();        In i = new In(p);        Out o = new Out(p);        new Thread(i).start();        new Thread(o).start();        new Thread(i).start();        new Thread(o).start();    }}

停止线程:
1、线程中的代码执行完毕后就会结束线程
2、线程中有循环,只要结束循环就能结束线程
一般可以采取标记的方式来结束循环:

public void run() {    "在外面控制flag的值来控制结束"    while (flag) {            System.out.println("aaaaaaaaaaa");    }}

清除线程异常状态:
原因:当线程处于冻结状态的时候,无法读取到结束标记,此时线程不会结束,造成程序无法结束
解决方法:
t.interrupt();(t是Thread类或子类的对象)强制清除冻结状态(并不是终结线程,只是让线程恢复运行),让线程能够继续运行,此时能够读取结束标记,线程结束,并且抛出异常

守护线程:
1、在start()之前调用
2、当正在运行的线程都是守护线程的时候,Java虚拟机退出
t.setDaemon();

join():等待线程终止
当A线程中执行到B线程join()方法时,A就会等待B线程执行完毕,在此期间不会执行A中的代码,也不会和B抢夺cpu执行权,直到B线程执行完毕
但是若有C线程也在执行,C会不断执行,会和B线程抢夺cpu执行权
作用:临时加入线程

懒汉式实例解析:

public class LanHanShi {    "//私有化构造函数,使得调用者不能创建对象"    private LanHanShi() {    }    "//先声明对象,并不创建,由于方法是static,所以声明对象时候也是static"    private static LanHanShi lan;    "//设置静态函数,获取返回对象"    public static LanHanShi getLanHanShi() {        "//二次判断,由于第一次执行完后之后lan已经有对象不为null,这时候就不用执行内部代码,提高效率"        if (lan == null) {            "//同步,由于操作语句多于1条,当出现多个线程的时候就会导致创建多个对象,锁使用的是函数所在类的字节码文件, 因为他不需要对象,但要声明为静态,对象静态生命周期过长,尽量少使用"            synchronized (LanHanShi.class) {                if (lan == null) {                    "创建对象"                    lan = new LanHanShi();                }            }        }        return lan;    }    public void show() {        System.out.println("show");    }}public class Demo {    private static int ticket = 100;    public static void main(String[] args) {        "/**         * 懒汉式         */"        LanHanShi lan = LanHanShi.getLanHanShi();        lan.show();    }}

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

0 0
原创粉丝点击