多线程
一,并发和并行
.并行:在同一时刻,有多个指令在多个cpu上同时执行
.并发:在同一时间,有多个指令在一个cpu上交替执行
二,进程和线程
进程:是正在运行的软件
.独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度独立单位。
.动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡。
.并发性:任何进程都可以同其他进程一起并发执行
三,线程:是进程中的单个顺序控制流,是一条执行路径
.单线程:一个进程如果只有一条执行路径,则称为单线程程序
四,多线程的实现方案
.继承Thread类
.实现Runnable接口的方式进行实现
.利用callable和future接口方式实现
两个小问题?
.为啥要重写run()方法
-----因为run()是用来封装被线程执行的代码
.run()方法和Start()方法的区别?
-----run():封装线程执行的代码直接调用,相当于普通方法的调用,并没有开启线程
-----start():启动线程然后由JVM调用此线程的run()方法
方法2.实现Runnable接口
.定义一个类MyRunnable类实现Runnable接口
.在MyRunnable中重写run()方法
.创建MyRunnable类的对象
.创建Thread类的对象,把MyRunable对象作为构造方法的参数
.启动线程
方式3:实现callble和Future
.定义一个MyCallable实现Callabke接口
.在Mycallable中重写call方法
.创建Mycallable类的对象
.创建Future的实现类FutureTask对象,把Mycallable对象作为构造方法的参数
.创建Thread类的对象,把FutrueTask作为构造方法的参数
.启动线程
.再调用get方法(),就可以获取线程结束后的结果
三种方式的对比
| 优点 | 缺点 | |
| 实现Runnable,callable接口 | 扩展性强,实现接口的同时还可以继承其他类 | 编程复杂,不能直接使用Thread类中的方法 |
| 继承Thread接口 | 编程简单,可以直接使用Thread类中的方法 | 可扩展性差,不能再继承其它类 |
Runnable
package com.guancun.thread.myrunnable; //实现runnable接口创建线程 public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"开启了"+i+"条线程!!"); } } public static void main(String[] args) { //创建参数对象 MyRunnable myRunnable = new MyRunnable(); //创建了一个线程对象,并将参数传递给这个线程 Thread thread1 = new Thread(myRunnable); Thread thread2 = new Thread(myRunnable); thread1.start(); thread2.start(); } }
Callable
package com.guancun.thread.mycallable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<Object> { @Override public Object call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println("表白" + i); } return "答应了"; } public static void main(String[] args) throws ExecutionException, InterruptedException { //创建Mycallable对象,线程开启后要执行里面的call()方法 MyCallable myCallable = new MyCallable(); //可以获取线程执行完毕后的结果,也可以作为参数传递给Thread对象 FutureTask futureTask = new FutureTask(myCallable); //创建线程对象 Thread thread = new Thread(futureTask); //开启线程 thread.start(); System.out.println(futureTask.get()); } }
Thread
package com.guancun.thread.mythread; public class MyThread extends Thread{ //重写run方法 @Override public void run() { for (int i = 0; i <1000 ; i++) { System.out.println("开启了"+i+"条线程!!"); } } } package com.guancun.thread.mythread; public class DemoTest { public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); //myThread1.run();表示创建对象调用方法,没有开启线程。 myThread1.start(); myThread2.start(); } }
四,获取和设置线程名称
获取线程的名字
.String getName():返回此线程的名称
Thread类中设置线程的名字
.void setName(String name):将此线程的名称更改为name
.通过构造方法也可以设置线程名称
五,获取线程的对象
.public static Thread currenthread()返回当前正在执行的线程的引用
六,线程休眠
.public static void sleep(Long time):让线程休眠指定时间(单位:毫秒)
七,线程调度
多线程的并发运行
计算机中在任何时刻只能执行一条机器指令,每个线程只有获得cpu的使用权才能执行代码,各个线程轮流获得cpu的使用权,分别执行各自的任务
八, 线程有两种调度模型
.分时调度模型:所有线程轮流使用cpu的使用权,平均分配每个线程使用cpu的时间片
.抢占式调度模型:优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个优先级高的线程获取cpu时间片相对多一些
九,线程的优先级(优先级越高,抢到使用权越大)
.public final void setPriority(int newPriority) 设置线程优先级
.public final int getPriority() 获取线程优先级
十,后台线程/守护线程
.public final void setDaemon (bolean on):设置为守护线程
package com.guancun.thread.protectthread; public class ProtectThread1 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"@@@"+i); } } } package com.guancun.thread.protectthread; public class ProtectThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"@@@"+i); } } } package com.guancun.thread.protectthread; public class ProtectThread implements Runnable{ public static void main(String[] args) { ProtectThread1 T1 = new ProtectThread1(); ProtectThread2 T2 = new ProtectThread2(); Thread t1 = new Thread(T1); Thread t2 = new Thread(T2); t1.setName("女神"); t2.setName("备胎"); t2.setDaemon(true); t1.start(); t2.start(); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"@@@"+i); } } }
十一,线程安全
买票案例
.多线程操作共享数据
如何解决线程安全问题
.基本思想:让程序没有安全问题的环境
怎么实现?
.把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个执行即可。
同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
.格式
synchronized(任意对象){
多条语句操作共享数据的代码
}
.默认情况下锁是打开的,只要有一个代码进去锁就关闭
.当线程执行完出来锁才会打开
同步锁的好处和坏处
.好处:解决了多线程数据安全问题
.坏处:耗费资源,降低程序运行效率率
同步方法
同步方法:就是把synchronized关键字加到方法上
.格式:
修饰符 synchroized 返回值类型 方法名(方法参数)()
同步代码快和同步方法区别
.同步代码块可以锁指定代码,同步方法是锁住方法中所有代码
.同步代码块可以指定锁对象,同步方法不能指定锁对象
同步方法的锁对象是什么?
.this
同步静态方法:就是synchronized关键字加到静态方法上。
.格式:
修饰符 static Synhronized 返回值类型方法名(方法参数){}
同步静态方法的锁对象是什么?
类名.class
Locks锁
JDK5以后提供的一个新的锁对象Lock.
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock ():获得锁 .void unlock ():释放锁
Lock 是接口不能实例化,这里采用它的实现类ReentrantLock来实例化
死锁:线程死锁是由于两个或者多个线程互相持有对方所需资源,导致这些线程处于等待状态,无法前往执行
生产者和消费者
等待和唤醒方法
为了体现产生和消费过程中的等待和唤醒,Java就提供,了几个方法供我们使用,这几个方法在object类中,Object类的等待和唤醒方法;
| 方法名 | 说明 |
| void wait () | 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifall |
| void notify() | 唤醒正在等待对象监视器的单个线程 |
| void notifyAll | 唤醒正在等待对象监视器的所有线程 |
package com.guancun.thread.producers_consumers; public class Desk { //false桌子上没有食物,反之TRUE有 public static boolean flag = false; //生产数量 public static int count = 10; //锁 public static final Object lock = new Object(); } package com.guancun.thread.producers_consumers; //生产者 public class Cooker extends Thread{ @Override public void run() { while (true){ synchronized (Desk.lock) { //判断做了几个食物了 if (Desk.count == 0) { break; } else { if (Desk.flag) { //厨师等待 try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println("厨师正在加工食物"); Desk.flag = true; Desk.lock.notifyAll(); } } } } } } package com.guancun.thread.producers_consumers; //消费者 public class FoodDie extends Thread{ @Override public void run() { while (true){ synchronized (Desk.lock){ if (Desk.count==0){ break; }else { //判断桌子上是否有食物 if (Desk.flag){ System.out.println("消费者开始吃食物"); //吃完桌子变为false Desk.flag = false; //通知厨师再做 Desk.lock.notifyAll(); Desk.count--; }else { //消费者等待 try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } package com.guancun.thread.producers_consumers; //测试 public class DemoTest { public static void main(String[] args) { Cooker cooker = new Cooker(); cooker.start(); FoodDie foodDie = new FoodDie(); foodDie.start(); } }
改进
package com.guancun.thread.producers_consumers; public class Desk2 { //false桌子上没有食物,反之TRUE有 private boolean flag; //生产数量 private int count ; //锁 private final Object lock = new Object(); public Desk2() { this(false,10); } public Desk2(boolean flag, int count) { this.flag = flag; this.count = count; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public Object getLock() { return lock; } } package com.guancun.thread.producers_consumers; //生产者 public class Cooker2 extends Thread{ private Desk2 desk; public Cooker2(Desk2 desk) { this.desk=desk; } @Override public void run() { while (true){ synchronized (desk.getLock()) { //判断做了几个食物了 if (desk.getCount() == 0) { break; } else { if (desk.isFlag()==true) { //厨师等待 try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println("厨师正在加工食物"); desk.setFlag(true); desk.getLock().notifyAll(); } } } } } } package com.guancun.thread.producers_consumers; //消费者 public class FoodDie2 extends Thread{ private Desk2 desk; public FoodDie2(Desk2 desk) { this.desk=desk; } @Override public void run() { while (true){ synchronized (desk.getLock()){ if (desk.getCount()==0){ break; }else { //判断桌子上是否有食物 if (desk.isFlag()){ System.out.println("消费者开始吃食物"); //吃完桌子变为false desk.setFlag(false); //通知厨师再做 desk.getLock().notifyAll(); desk.setCount(desk.getCount()-1); }else { //消费者等待 try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } package com.guancun.thread.producers_consumers; //测试 public class DemoTest2 { public static void main(String[] args) { Desk2 desk = new Desk2(); Cooker2 cooker = new Cooker2(desk); cooker.start(); FoodDie2 foodDie = new FoodDie2(desk); foodDie.start(); } }
组塞队列实现等待唤醒机制
阻塞队列继承结构
Iterable ---------->collection---------->queue---------->BlockingQueue==============1.ArrayBlockingQueue实现类 2.LinkBlockingQueue实现类
接口 接口 接口 接口
组塞队列的等待唤醒机制
BlockingQueue的核心方法:
put(Object):将参数放入阻塞队列,如果放不进会阻塞
take():取出第一个数据,取不到会阻塞
常见的BlockingQueue:
ArrayBlockingQueue
ArrayBlockingQueue:底层数组,有界
LinkBlockingQueue
LinkBlockingQueue:底层链表,无界,但不是真正的无界,最大为int的最大值
线程状态

虚拟机中线程的六种状态
新建状态(NEW) -------------》 创建线程对象
就绪状态(RUNNABLE)--------------> start方法
阻塞状态 (BLOCKED) --------------> 无法获得锁对象
等待状态(WAITING) ----------> WAIT 方法
计时等待 (TIMED_WAITING)----------->SLEEP方法
结束状态 (TERMINATED)----------->全部代码运行完毕
线程池
步骤:
package com.guancun.thread.threadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyTresdPool { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool();//创建一个默认的线程池对象 大小为int最大值 //Executors可以帮助我们创建线程池 //ExecutorService可以帮助我们控制线程池 executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"在执行"); }); executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"在执行i"); }); executorService.shutdown(); } }
创建自定义线程池
ThreadPoolExecutor pool = new ThreadpoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数解析:
参数一:核心线程数量------》不能小于0 参数二:最大线程数------》不能小于0(最大数量》=核心数量) 参数三:空闲线程最大存活时间-----》不小于0 参数四:时间单位 ------》时间单位
参数五:线程队列-----》不为null 参数六:创建线程工厂------》不为null 参数七:任务拒绝策略------》不为null
任务拒绝策略
ThreadPoolExecutor.Abortpolicy 丢弃任务并抛出RejecteddExecutionExecution异常。是默认策略。
ThreadPoolExecutor.DiscardPolicy 丢弃任务但是不抛出异常这是不推荐的做法
ThreadPoolExecutor.DiscardPolicy 抛弃队列中等待最久的任务然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunspolicy 调用任务的run()方法绕过线程直接执行
Volatile关键字
强制线程每次在使用的时候,都会看一下共享区最新的值
原子性:
所谓原子性是指在一次操作或多次操作中,要么所有的操作全部都得执行并且不会受到任何因素干扰而中断,要么所有操作都不执行,多个操作是一个不可分割的整体。
原子类AtomicIntrger:
构造方法:
public AtomicInteger():初始化一个默认参数值0的原子型Integer
public AtomicInteger():初始化一个指定值的原子型Integer
成员方法:
| int get() | 获取值 |
| int getAndIncrement() | 以原子方式将当前值加1,注意这里返回的是自增前的值 |
| int addAndGet() | 以原子方式将当前值加1,注意这里返回的是自增后的值 |
| int addAndGet(int data) | 以原子方式将输入的数值与实例中得值(AtomicInteger)相加,并且返回结果 |
| int getAndSet(int Value) | 以原子方式设置为newValue的值,并返回值 |

浙公网安备 33010602011771号