黑马程序员——多线程

------- 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方法,完成该线程的业务逻辑
	@Override
	public 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 {
	@Override
	public 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 {

	@Override
	public 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 {

	@Override
	public 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;
	}
	
	@Override
	public 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;
	}

	@Override
	public 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方法,完成生产逻辑
	@Override
	public 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方法,完成消费逻辑
	@Override
	public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值