<h1 style="margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: Arial; line-height: 26px;"><span style="font-family: SimHei;"><span style="font-family: Simsun;">------- </span><a target=_blank target="_blank" href="http://www.itheima.com/" style="color: rgb(51, 102, 153); text-decoration: none; font-family: Simsun;">android培训</a><span style="font-family: Simsun;">、</span><a target=_blank target="_blank" href="http://www.itheima.com/" style="color: rgb(51, 102, 153); text-decoration: none; font-family: Simsun;">java培训</a><span style="font-family: Simsun;">、期待与您交流!----------</span></span></h1>
线程与进程
线程:是依赖于进程的执行绪(执行路径/控制单元),是程序使用CPU的基本单位。进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域。多进程:同一时间段内执行多个任务。同一时刻只能执行一个任务。如Windows为代表的操作系统。 多进程并不提高某个程序的执行速度,仅仅是提高了CPU的使用率。真正的多进程执行是指多核同时计算。单线程:一个进程中,只有一个线程执行。多线程:同一个进程中,多个线程执行。这多个线程共享该进程资源(堆内存与方法区),栈内存独立,即每一个线程占用一个栈。
线程的两种调度方式
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。抢占式调度模型 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个。(线程随机性)注:java使用的是抢占式调度模型
线程并行与线程并发
线程并行:正常的多线程执行就是线程并行。即逻辑上同一时间同时运行。 就是在执行时每个线程都在不断地根据自己的优先级在抢CPU
JVM的启动支持多线程线程并发:由于线程抢占而不应出现的某一时刻的线程及相关数据状态。如并发修改异常的产生。
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
二:多线程的实现方式以及相关方法
一)多线程的实现方式
方式一:继承Thread类
自定义线程类继承Thread类。
重写run方法。run方法内为该线程执行代码。将其理解为其他线程的main方法,即该线程的执行入口。
使用:
创建线程对象
开启线程,即调用start方法,该方法会自动调用这个线程的run方法。
方式二:实现Runnable接口
定义自己的类(线程执行目标类),实现Runnable接口创建线程执行目标类对象使用线程执行目标对象创建线程对象调用线程的start方法开启线程
方式一和方式二的代码实现 用到的方法
构造方法:
public Thread(Runnable target) 指定线程执行目标
普通方法:
public final String getName()
public final void setName(String name)
public static Thread currentThread()
public void run()
public void start()
重写public String toString()
实现多线程的方式:
public void start() 开启线程public final String getName() 获取线程名称public final void setName(String name) 设置线程名称public static Thread currentThread() 获取当前的线程
代码演示
package cn.itcast; public class Demo2 { public static void main(String[] args) { method(); System.out.println("========="); method2(); } //多线程创建方式一 public static void method(){ MyThread mt = new MyThread("唐嫣"); //mt.setName("唐嫣"); MyThread mt2 = new MyThread("赵丽颖"); //mt.setName("赵丽颖"); MyThread mt3 = new MyThread("斯琴高丽"); //mt.setName("斯琴高丽"); /* * 如果调用run方法,就是调用了一个普通对象的普通方法,没有新线程 * mt.run(); * mt2.run(); * mt3.run(); */ //调用start方法开启线程 mt.start(); mt2.start(); mt3.start(); } //多线程创建方式二 public static void method2(){ //创建多线程执行目标类对象 MyRunnable mr = new MyRunnable(); //通过线程执行目标类对象,创建线程对象 Thread t1 = new Thread(mr,"唐嫣"); Thread t2 = new Thread(mr,"诗诗"); Thread t3 = new Thread(mr,"颖颖"); //开启线程 t1.start(); t2.start(); t3.start(); } }
MyThread的代码
package cn.itcast; /* * 定义自己的线程类 */ public class MyThread extends Thread { //空参构造 public MyThread() { super(); } //有参构造 public MyThread(String name) { super(name); } //重写该线程中的"main"方法,实际上就是run方法 @Override public void run() { String name2 = getName(); for (int i = 0; i < 100; i++) { System.out.println(name2+"--"+i); } } }
MyRunnable代码
从上面可以发现方式一和方式二的区别package cn.itcast; /* * 定义线程执行目标类 */ public class MyRunnable implements Runnable { //重写run方法,供线程调用 @Override public void run() { //获取当前执行的线程 Thread currentThread = Thread.currentThread(); String name = currentThread.getName(); for (int i = 0; i < 100; i++) { System.out.println(name+"=="+i); } } }
方式一:如果一个类明确了自己的父类,那么它就不可以再继承Thread。因为java不允许类的多继承。
方式二:可以让业务类不再继承Thread而专注于业务继承其他类,避免了单继承的局限性。
二)相关方法
1)线程的优先级
概念:线程优先级代表了抢占CPU的能力。优先级越高,抢到CPU执行的可能性越大。
优先级相关方法:
public final void setPriority(int newPriority) 优先级取值:1-10
public final int getPriority()
代码实现
package cn.itcast2; /* * 优先级的范围: * 1-10 默认5 */ public class Demo { public static void main(String[] args) { MyThread mt = new MyThread("唐嫣"); MyThread mt2 = new MyThread("诗诗"); mt.setPriority(10); mt2.setPriority(1); mt.start(); mt2.start(); } }
2)线程休眠
概念:指定线程休眠一定时间,进入等待状态。在该段时间结束后,线程重新可执行。
代码实现
package cn.itcast3; /* * 线程休眠 * 相关方法 * public static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) throws InterruptedException */ public class Demo { public static void main(String[] args) { MyThread mt = new MyThread("唐嫣"); MyThread mt2 = new MyThread("诗诗"); mt.setPriority(10); mt2.setPriority(1); mt.start(); mt2.start(); } }
MyThread的代码
package cn.itcast3; public class MyThread extends Thread { public MyThread() { super(); } public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { if(i==20) { try { //定义线程休眠时间为10000毫秒 Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName()+":"+i); } } }
3)加入线程
概念:等待某加入的线程终止后再执行。
代码实现
package cn.itcast4; /* * 线程加入 * public final void join() 等待该线程终止。 throws InterruptedException 即 调用方法时所在的线程,等待调用方法的线程执行完毕 */ public class Demo { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread("唐嫣"); MyThread mt2 = new MyThread("诗诗"); mt.setPriority(10); mt2.setPriority(1); mt.start(); mt2.start(); mt.join(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
4)线程礼让
概念:暂停当前正在执行的线程对象,并执行其他线程。
代码实现
package cn.itcast5; /* * 线程礼让 * public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 */ public class Demo { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread("唐嫣"); MyThread mt2 = new MyThread("诗诗"); mt.start(); mt2.start(); } }
package cn.itcast5; public class MyThread extends Thread { public MyThread() { super(); } public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); //定义线程礼让,直接用Thread调用方法 Thread.yield(); } } }
5)守护线程
概念:将该线程标记为守护线程。被守护的线程执行完毕时,程序即停止运行。守护线程执行完毕不影响被守护线程。
代码实现:
package cn.itcast6; /* * 守护线程 * public final void setDaemon(boolean on) 设置守护线程 */ public class Demo { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread("野飞"); MyThread mt2 = new MyThread("腾飞"); MyThread mt3 = new MyThread("亦菲"); mt.setPriority(10); mt2.setPriority(10); mt3.setPriority(1); //mt和mt2位守护线程 mt.setDaemon(true); mt2.setDaemon(true); mt.start(); mt2.start(); mt3.start(); } }
6)线程中断
概念:stop方法已过时,通常使用interrupt方法。
代码实现:
package cn.itcast7; /* * 线程中断 * public final void stop() 已过时 结束线程。杀掉线程! * public void interrupt() 中断线程。 */ public class Demo { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread("野飞"); MyThread mt2 = new MyThread("腾飞"); mt.start(); mt2.start(); Thread.sleep(3000); //stop已经过时,直接杀死线程 // mt.stop(); // mt2.stop(); //线程中断 mt.interrupt(); mt2.interrupt(); } }
三、安全问题与lock锁
1)线程的安全问题
概念:在多个线程同时运行时发生的异常情况统称为线程安全问题。线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
产生原因和前提:
线程随机访问性
有多个线程并行
多个线程有共享数据
处理方式:多个线程操作了共享数据
使用java提供的同步机制,使某一线程的完整动作执行完毕,其他线程再进行操作
为了保证同步,我们提供了原子性的操作。
同步代码块的格式:
2)lock锁synchronized(锁对象){//该对象可以是任意对象
需要同步的代码;
}
代码实现:
package cn.itcast8; public class Demo { public static void main(String[] args) { Tickets tickets = new Tickets(); Thread thread = new Thread(tickets,"柳岩"); Thread thread2 = new Thread(tickets,"花千骨"); thread.start(); thread2.start(); } }
同步方法的锁是其所在对象;静态同步方法的锁就是类本身。package cn.itcast8; /* * 定义线程执行目标类:票 */ public class Tickets implements Runnable { int number = 100; Object lock = new Object(); //重写run方法,卖票的方法 @Override public void run() { while(true) { //使用synchronized将一个原子性操作包裹,是其同步,不同的线程需要使用相同的锁 synchronized (lock) { try { //使线程睡眠10毫秒,加大出现安全问题的几率 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //只要有票就可以卖 if(number>0) { //将数字打印即卖票的过程 System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票"); //每次卖完一张票可以减少1 number--; } } } } }
package cn.itcast9; /* * 同步方法的锁是其所在对象。 * 静态同步方法的锁就是类本身。 */ public class Demo { public static void main(String[] args) { Tickets tickets = new Tickets(); Thread thread = new Thread(tickets,"柳岩"); Thread thread2 = new Thread(tickets,"花千骨"); thread.start(); thread2.start(); } }
package cn.itcast9; /* * 定义线程执行目标类:票 */ public class Tickets implements Runnable { static int number = 100; Object lock = new Object(); int x = 0; //重写run方法,卖票的方法 @Override public void run() { while(true) { if(x%2==0) { synchronized (Tickets.class) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(number>0) { System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票"); number--; } } }else { method(); } x++; } } // //同步方法:直接在方法声明上加synchronized同步关键字修饰 // public synchronized void method() { synchronized (lock) { // try { // Thread.sleep(10); // } catch (InterruptedException e) { // e.printStackTrace(); // } // if(number>0) { // System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票"); // number--; // } } // } //静态同步方法:直接在方法声明上加synchronized同步关键字修饰 public static synchronized void method() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(number>0) { System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票"); number--; } } }
另外一种解决同步的解决方案
package cn.itcast2; /* * Lock锁: * 另外一种同步解决方案 * * ReentrantLock:Lock接口的子类对象 * * void lock() 上锁 * void unlock() 解锁 */ public class Demo { public static void main(String[] args) { Tickets tickets = new Tickets(); Thread thread = new Thread(tickets,"柳岩"); Thread thread2 = new Thread(tickets,"花千骨"); thread.start(); thread2.start(); } }
package cn.itcast2; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 定义线程执行目标类:票 */ public class Tickets implements Runnable { int number = 100; Lock lock = new ReentrantLock(); //重写run方法,卖票的方法 @Override public void run() { while(true) { lock.lock(); try { //使线程睡眠10毫秒,加大出现安全问题的几率 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //只要有票就可以卖 if(number>0) { //将数字打印即卖票的过程 System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票"); //每次卖完一张票可以减少1 number--; } lock.unlock(); } } }
3)等待唤醒机制
当出现对同一资源的生产与消费时,可以使用多线程完成对同一资源的操作。而消费者需要等待生产者生产后才能消费,生产者也需要等待消费者消费后才能生产。于是出现了生产者消费者问题。这时可以使用等待唤醒机制完成相关需求。
涉及方法:
涉及并非是Thread类的方法,而是Object类的两个方法:因为锁可以为共享数据本身可以是任意的对象,在runnable中进行等待唤醒当前所在线程。
等待:
public final void wait() throws InterruptedException
让当前线程进入等待状态,如果线程进入该状态,不唤醒或打断,不会解除等待状态。
进入等待状态时会释放锁。
唤醒:
public final void notify()唤醒正在等待的线程。继续等待之后的代码执行。
sleep与wait的区别:
sleep指定时间,wait可指定可不指定。
sleep释放执行权,不释放锁。因为一定可以醒来。
wait释放执行权与锁。
代码实现生产消费交替执行的
package cn.itcast4; /* * 定义共享数据Person类 */ public class Person { private String name; private int age; 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 + "]"; } }
package cn.itcast4; /* * 生产者的生产执行目标 */ public class PRunnable implements Runnable { //定义Person类型的成员变量,用于传入与其他线程共享的数据 private Person p; //定义标志位,用来区别每次赋值的人名与年龄 int x = 0; public PRunnable(Person p) { this.p = p; } @Override public void run() { while(true) { synchronized (p) { if(x%2==0) { p.setName("柳岩"); p.setAge(32); } else { p.setName("唐嫣"); p.setAge(28); } x++; } } } }
package cn.itcast4; /* * 消费者的生产执行目标 */ public class CRunnable implements Runnable { //定义Person类型的成员变量,用于传入与其他线程共享的数据 private Person p; public CRunnable(Person p) { this.p = p; } @Override public void run() { while(true) { synchronized (p) { System.out.println(p.getName() + ":" + p.getAge()); } } } }
package cn.itcast4; /* * 生产者为一个生产线程 P * 消费者为一个消费线程 C * * 共享数据: * 一个Person对象 * * 消费:打印该对象的姓名与年龄 * 生产:为这个对象的年龄和姓名赋不同的值 */ public class Demo { public static final Object lock = new Object(); public static void main(String[] args) { //创建共享数据 Person p = new Person("唐嫣", 28); //创建生产者与消费者的线程执行目标类对象 PRunnable pr = new PRunnable(p); CRunnable cr = new CRunnable(p); //创建线程对象 Thread PThread = new Thread(pr); Thread CThread = new Thread(cr); //开启线程 PThread.start(); CThread.start(); } }
4)单列设计模式
内存当中只存在一个实例对象。
单例设计模式要求。
构造方法私有化
定义了自己类型的静态私有成员变量
对外提供公共的、静态的获取实例方法
实例创建方式分为懒汉式与饿汉式
懒汉式时,需要考虑线程安全问题
代码实现:
package cn.itcast3; import java.io.IOException; public class Demo { public static void main(String[] args) throws IOException { Person p = Person.getInstance(); Person p2 = Person.getInstance(); System.out.println(p==p2); Runtime runtime = Runtime.getRuntime(); //notepad记事本 runtime.exec("notepad"); } }
饿汉式
package cn.itcast3; /* * 饿汉式单列设计 */ public class Person { //构造方法私有化 Person(){} //定义私有静态成员变量 private static Person p = new Person(); //提供公共静态的方法,用于获取单列的对象 getInstance获取实例化对象 public static Person getInstance(){ return p; } }
懒汉式
package cn.itcast4; /* * 懒汉式单列设计 */ public class Person { //定义一个锁 public static Object lock = new Object(); //构造方法私有化 private Person(){} //定义私有静态成员变量 private static Person p; //第一种方式 // //提供公共静态的方法,用于获取单列的对象 // public static Person getInstance(){ // synchronized(lock){ // if(p==null){ // p = new Person(); // } // } // return p; // } //第二种方式 //在考虑线程安全的前提下又考虑到了效率问题 // public static Person getInstance(){ // // if(p==null){ // synchronized(lock){ // if(p==null){ // p=new Person(); // } // } // } // return p; // // } //第三种方式 public static synchronized Person getInstance(){ if(p==null){ p=new Person(); } return p; } }