黑马程序员——多线程

来源:互联网 发布:python的join函数 编辑:程序博客网 时间:2024/06/04 00:15

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

(一)基本概念

线程:是依赖于进程的执行绪(执行路径/控制单元),是程序使用CPU的基本单位。

进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域。

多进程:同一时间段内执行多个任务。同一时刻只能执行一个任务。如Windows为代表的操作系统。

多进程并不提高某个程序的执行速度,仅仅是提高了CPU的使用率。真正的多进程执行是指多核同时计算。

单线程:一个进程中,只有一个线程执行。

多线程:同一个进程中,多个线程执行。这多个线程共享该进程资源(堆内存与方法区),栈内存独立,即每一个线程占用一个栈。

线程两种调度模型:

分时调度模型   所有线程轮流使用 CPU 的使用权,平均分配每个线程占用CPU 的时间片。

抢占式调度模型   优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个。(线程随机性)

Java使用的为抢占调度模型。

线程并行与线程并发

线程并行:正常的多线程执行就是线程并行。即逻辑上同一时间同时运行。

线程并发(异常):由于线程抢占而不应出现的某一时刻的线程及相关数据状态。如并发修改异常的产生。

JVM的启动支持多线程:

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程”,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。

 

 

线程生命周期

 
(二)线程Thread类常用方法

构造方法:

public Thread()

public Thread(Runnable target)

public Thread(String name)

public Thread(Runnable target,String name)

主要方法:

public final String getName()

public final void setName(String name)

public static Thread currentThread()

public void run()

public void start()


开启一个线程的步骤:
         创建一个普通的线程类对象
          调用线程的start方法开启线程。此时才真正开始执行一个新的线程。
 
          public void run()  该线程要执行的代码。相当去那个线程的main方法
          该方法不是用来调用的,是用来重写的。
          public void start()  使该线程开始执行;
          Java 虚拟机调用该线程的 run 方法。

线程的第一种开启方式:
        自定义线程类继承Thread类。
        重写run方法。run方法内为该线程执行代码。将其理解为其他线程的main方法,即该线程的执行入口。
        使用:
        创建线程对象
        开启线程,即调用start方法,该方法会自动调用这个线程的run方法。
开启方式二:实现Runnable接口
    自定义Runnable 的子类(非线程类)。
    重写run方法。run方法内为该类对象所在线程的执行代码。
    同样可将其理解为其他线程的main方法,即该线程的执行入口。
    使用:
    创建Runnable的子类对象。
    使用Runnable的子类对象创建线程对象。

    开启线程,即调用start方法,该方法会自动调用这个线程的run方法。

方式一与方式二的区别

-方式一:当类去描述事物,事物中有属性和行为。如果行为中有部分代码需要被多线程所执行,同时还在操作属性。就需要该类继承Thread类,产生该类的对象作为线程对象。可是这样做会导致每一个对象中都存储一份属性数据。无法在多个线程中共享该数据。加上静态,虽然实现了共享但是生命周期过长。
-方式一:如果一个类明确了自己的父类,那么它就不可以再继承Thread。因为java不允许类的多继承。

-方式二:将线程与运行的业务逻辑分离,可以让多个线程共享业务逻辑中的数据。
-方式二:可以让业务类不再继承Thread而专注于业务继承其他类,避免了单继承的局限性。

public class Demo01_Thread {public static void main(String[] args) {//创建线程对象MyThread mt = new MyThread();mt.setName("赵丽颖的线程");//开启线程,即调用线程的start方法的过程,之后会自动调用run方法mt.start();//返回正在执行的线程Thread currentThread = Thread.currentThread();//获取线程名称System.out.println(currentThread.getName());for (int i = 0; i < 10; i++) {System.out.println(currentThread.getName()+"今天又敲代码了"+i);}}}
<pre name="code" class="java">public class MyThread extends Thread {//重写run方法,完成该线程的业务逻辑@Overridepublic void run() {for (int i = 0; i < 10; i++) {//返回正在执行的线程Thread currentThread = Thread.currentThread();System.out.println(currentThread.getName()+"今天又敲代码了"+i);}}}


方式二代码

<pre name="code" class="java">public class Demo02_Runnable {public static void main(String[] args) {//创建线程执行目标类对象MyRunnable mr = new MyRunnable();Thread thread = new Thread(mr, "唐嫣");thread.start();for (int i = 0; i < 20; i++) {//获取当前执行的线程Thread thisThread = Thread.currentThread();System.out.println(thisThread.getName()+"又敲代码了"+i);}}}


public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 20; i++) {//获取当前执行的线程Thread thisThread = Thread.currentThread();System.out.println(thisThread.getName()+"又敲代码了"+i);}}}


一般普通方法

 <pre name="code" class="java">/* * Thread:线程优先级 * public final void setPriority(int newPriority)  设置调用方法线程的优先级 * public final int getPriority()  返回调用方法线程的优先级 *  * 默认优先级:5 * 最大优先级:10 * 最小优先级:1 */public class Demo01_Priority {public static void main(String[] args) {MyThread mt = new MyThread();mt.setName("唐嫣");MyThread mt2 = new MyThread();mt2.setName("杨幂");MyThread mt3 = new MyThread();mt3.setName("屠呦呦");System.out.println(mt.getPriority());System.out.println(mt2.getPriority());System.out.println(mt3.getPriority());mt.setPriority(10);mt2.setPriority(1);mt.start();mt2.start();mt3.start();}}

/* * Thread:线程休眠 * public static void sleep(long millis) throws InterruptedException  在指定的毫秒数内让当前正在执行的线程休眠 *  * 该方法为静态方法,不能直接使用线程对象调用。 * 如果想让某个线程休眠,则在该线程的代码中加入该休眠 */public class Demo01_Sleep {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();mt.setName("唐嫣");MyThread mt2 = new MyThread();mt2.setName("杨幂");MyThread mt3 = new MyThread();mt3.setName("屠呦呦");mt.start();mt2.start();mt3.start();Thread.sleep(2000);for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"又敲代码了"+i);}}}<pre name="code" class="java">public class MyThread extends Thread {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < 20; i++) {System.out.println(currentThread().getName()+"又敲代码了"+i);}}}

 
/* * Thread:加入线程 * public final void join()  throws InterruptedException  加入线程,等待该线程终止。  *  * 等待其他线程的线程:  join方法代码所在的线程 *  * 被等待执行完毕的线程: 调用join方法的线程 */public class Demo01_join {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();mt.setName("唐嫣");mt.setPriority(1);MyThread mt2 = new MyThread();mt2.setName("杨幂");mt2.setPriority(10);mt.start();mt2.start();mt.join();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"又敲代码了"+i);}}}
/* * Thread:线程礼让 *  * public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 */public class Demo01_yield {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();mt.setName("唐嫣");MyThread mt2 = new MyThread();mt2.setName("杨幂");mt.start();mt2.start();}}<pre name="code" class="java">public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(currentThread().getName()+"又敲代码了"+i);Thread.yield();}}}


/* * Thread:守护线程 *  * public final void setDaemon(boolean on) 将线程设置为守护线程 * 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。  */public class Demo01_daemon {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();mt.setName("唐嫣");MyThread mt2 = new MyThread();mt2.setName("杨幂");mt.setDaemon(true);mt2.setDaemon(true);mt.start();mt2.start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"又敲代码了"+i);Thread.yield();}}}<pre name="code" class="java">
/* * Thread:线程中断 *  * public final void stop()  停止线程,杀死线程 * public void interrupt() 中断线程 *  * 如果该线程处于某种等待状态时,需要中断,则调用该方法。 * 调用方法后,停止等待的线程会抛出一个异常对象,此时只需要捕获该异常,处理异常代码 */public class Demo01_interrupt {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();mt.setName("唐嫣");MyThread mt2 = new MyThread();mt2.setName("杨幂");mt.start();mt2.start();Thread.sleep(1000);//mt.stop();//mt2.stop();mt.interrupt();mt2.interrupt();}}


(三)Java同步机制

lJava同步机制:为解决同步问题而提供的工具
原子性操作:
-在执行操作时,我们把一个完整动作可以称为一个原子性操作,是一个不可切割的动作。即不可被线程打断的操作。
synchronized 关键字:
-同步代码块格式:
-  synchronized(锁对象){//该对象可以是任意对象
-  需要同步的代码;
-  }
-锁:几个线程需要使用相同的锁对象进行同步操作,使用不同的锁是无法完成同步操作的。
-Synchronized内需要同步的代码即为一个原子性操作。
-同步方法:方法上声明,将所在对象作为默认锁,即this。
-同步静态方法:将方法所在类作为默认所,即XX.class。
优点:解决了多线程安全问题
缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。对于一个简单操作,单线程速度更快。

lLock锁同样可以完成代码同步的任务。
相较于synchronized方式,Lock锁的出现使同步操作更为灵活。无需使用限制性强的代码块。
Lock同样为抽象类,需要使用其子类ReentrantLock的对象完成方法调用。
主要方法:
-public voidlock()获取锁
-public void unlock() 释放锁

死锁:
l在多线程的代码编辑过程中,由于考虑得不够周全,会出现死锁的情况。
线程死锁代码:备注
原因分析:
-线程1将锁1锁住,线程2将锁2锁住,而线程1要继续执行锁2中的代码,线程2要继续执行锁1中的代码,但是此时,两个锁均处于锁死状态。最终导致两线程相互等待,进入无限等待状态。
-有同步代码块的嵌套动作。
解决方法:
-不要写同步代码块嵌套。
public class DeadLockThread extends Thread {boolean flag;//定义标记,用来指定要执行的代码public DeadLockThread(boolean flag) {this.flag = flag;}@Overridepublic void run() {if(flag) {//flag赋值为true时,执行的代码synchronized (Demo01_deadlock.锁1) {System.out.println("if中锁1");//try {//sleep(20);//} catch (InterruptedException e) {//e.printStackTrace();//}synchronized (Demo01_deadlock.锁2) {System.out.println("if中锁2");}}} else {//flag赋值为false时,执行的代码synchronized (Demo01_deadlock.锁2) {System.out.println("else中锁2");synchronized (Demo01_deadlock.锁1) {System.out.println("else中锁1");}}}}}public class Demo01_deadlock {public static Object 锁1 = new Object();public static Object 锁2 = new Object();public static void main(String[] args) {Thread thread = new DeadLockThread(true);Thread thread2 = new DeadLockThread(false);thread.start();thread2.start();}}


(四)单例设计模式
(1)保证类在内存中只有一个对象。
(2)怎么保证:
A:构造私有
B:自己造一个对象
C:提供公共访问方式
(3)两种方式:
A:懒汉式

public class Student {private Student(){}private static Student s = null;public synchronized static Student getStudent() {if(s == null) {s = new Student();}return s;}}





B:饿汉式
public class Student {private Student(){}private static Student s = new Student();public static Student getStudent() {return s;}}


(4)JDK的一个类本身也是单例模式的。
Runtime


(五)等待唤醒机制

l当出现对同一资源的生产与消费时,可以使用多线程完成对同一资源的操作。而消费者需要等待生产者生产后才能消费,生产者也需要等待消费者消费后才能生产。于是出现了生产者消费者问题。这时可以使用等待唤醒机制完成相关需求。

•等待唤醒机制涉及到的方法并非是Thread类的方法,而是Object类的两个方法:因为锁可以为共享数据本身可以是任意的对象,在runnable中进行等待唤醒当前所在线程。
等待:
-public final void wait() throws InterruptedException
-让当前线程进入等待状态,如果线程进入该状态,不唤醒或打断,不会解除等待状态。
-进入等待状态时会释放锁。
唤醒:
-public final void notify()
-唤醒正在等待的线程。
-继续等待之后的代码执行。
sleep与wait的区别:
-sleep指定时间,wait可指定可不指定。
-sleep释放执行权,不释放锁。因为一定可以醒来。
-wait释放执行权与锁。
(六)生产者消费者练习:
/* * 共享数据:一个Person对象 * 生产: 为这个Person对象的属性赋值 * 消费:取这个Person对象的属性值并打印 *  * 当生产者已经生产数据而没有被消费掉时:生产者等待消费者线程消费 * 反之亦然 * Object类的线程等待: * public final void wait() throws InterruptedException  让其共享数据所在的线程等待 * 该方法会释放掉锁。从哪里等待,唤醒后从哪里继续执行 * sleep方法不会释放锁,因为sleep方法是根据时间判定,是一定会醒的。 *  * Object类的线程唤醒: * public final void notify() 唤醒已经在共享数据上等待的线程 */public class Demo01 {public static void main(String[] args) {//创建共享数据Person p = new Person();//创建生产者线程执行目标类对象Runnable scr = new ShenChanRunnable(p);//创建消费者线程执行目标类对象Runnable xfr = new XiaoFeiRunnable(p);//通过线程执行目标类对象创建生产与消费线程对象Thread shengchan = new Thread(scr);Thread xiaofei = new Thread(xfr);//开启线程xiaofei.start();shengchan.start();}}public class Person {private String name;private int age;//定义成员变量,记录数据的生产消费状态:true代表有数据   false代表没有数据boolean flag;public Person() {super();}public Person(String name, int age) {super();this.name = name;this.age = age;}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;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}}//定义生产线程线程执行目标类public class ShenChanRunnable implements Runnable {//定义Person类型的成员变量,用于接收共享数据private Person p;//接收共享数据的构造方法public ShenChanRunnable(Person p) {this.p = p;}//定义一个标志位,用来标志生产内容int x = 0;//重写run方法,完成生产逻辑@Overridepublic void run() {//循环生产while(true) {//使用共享数据Person对象p作为锁synchronized (p) {//判断是否有数据if(p.flag) {//如果有数据,生产者线程就等待try {p.wait();} catch (InterruptedException e) {e.printStackTrace();}}//为共享数据的属性生产值if(x%2==0) {p.setName("汤圆");p.setAge(800);}else {p.setName("唐嫣");p.setAge(18);}x++;//重置共享数据状态p.flag = true;//唤醒其他线程p.notify();}}}}//定义消费线程线程执行目标类public class XiaoFeiRunnable implements Runnable {//定义Person类型的成员变量,用于接收共享数据private Person p;//接收共享数据的构造方法public XiaoFeiRunnable(Person p) {this.p = p;}//重写run方法,完成消费逻辑@Overridepublic void run() {//循环消费while(true) {//使用共享数据Person对象p作为锁synchronized (p) {//判断是否有数据if(!p.flag) {//如果没有数据,消费者线程就等待try {p.wait();} catch (InterruptedException e) {e.printStackTrace();}}//取共享数据的属性值,作为消费System.out.println(p.getName()+":"+p.getAge());//重置共享数据状态p.flag = false;//唤醒其他线程p.notify();}}}}



0 0