当前位置: 首页>后端>正文

java多线程、线程同步与线程池

1. 线程的基本概念

1.1 进程

任何的软件存储在磁盘中,运行软件的时候,OS使用IO技术,将磁盘中的软件的文件加载到内存,程序在能运行。

进程的概念 :?应用程序(typerpa,word,IDEA)运行的时候进入到内存,程序在内存中占用的内存空间(进程).

1.2 线程

线程(Thread) : 在内存和CPU之间,建立一条连接通路,CPU可以到内存中取出数据进行计算,这个连接的通路,就是线程.

一个内存资源 : 一个独立的进程,进程中可以开启多个线程 (多条通路)

并发: 同一个时刻多个线程同时操作了同一个数据

并行: 同一个时刻多个线程同时执行不同的程序

2. Java实现线程程序

今天之前的所有程序都有一个共性 : main启动之后,一条线走到底 (单线程)

2.1 java.lang.Thread类

一切都是对象,线程也是对象,Thread类是线程对象的描述类

实现线程程序的步骤 :

定义类继承Thread

子类重写方法run

创建子类对象

调用子类对象的方法start()启动线程:

//- 定义类继承Thread

//- 子类重写方法run

public class SubThread extends Thread {

? ? public void run(){

? ? ? ? for(int x = 0 ; x < 50 ;x++)

? ? ? ? ? ? System.out.println("run..."+x);

? ? }

}

public static void main(String[] args) {

? ? //创建线程程序

? ? SubThread subThread = new SubThread();

? ? //调用子类对象的方法start()启动线程

? ? //启动线程,JVM调用方法run

? ? subThread.start();

? ? for(int x = 0 ; x < 50 ;x++)

? ? ? ? ? ? System.out.println("main..."+x);

}

2.2 线程的内存图

java多线程、线程同步与线程池,第1张

2.3 Thread类方法

Thread类的方法 getName()返回线程的名字,返回值是String类型

public class ThreadName extends Thread {

? ? public void run (){

? ? ? ? System.out.println("线程名字:: "+ super.getName());

? ? }

}

? public static void main(String[] args) {

? ? ? ? ThreadName threadName = new ThreadName();

? ? ? ? //threadName.setName("旺财");

? ? ? ? threadName.start();

? ? ? ? ThreadName threadName1 = new ThreadName();

? ? ? ? //threadName1.setName("小强");

? ? ? ? threadName1.start();

? ? }

Thread类静态方法 : Thread currentThread()

静态调用,作用是返回当前的线程对象

"当前" , 当今皇上. 本地主机

//获取当前线程对象,拿到运行main方法的线程对象

Thread thread =? Thread.currentThread();

System.out.println("name::"+thread.getName());

Thread类的方法 join()

解释,执行join()方法的线程,他不结束,其它线程运行不了:

? ? public static void main(String[] args) throws InterruptedException {

? ? ? ? JoinThread t0 = new JoinThread();

? ? ? ? JoinThread t1 = new JoinThread();

? ? ? ? t0.start();

? ? ? ? t0.join();

? ? ? ? t1.start();

? ? }

Thread类的方法 static yield()

线程让步,线程把执行权让出

? ? public void run() {

? ? ? ? for(int x = 0 ; x < 50 ;x++){

? ? ? ? ? ? Thread.yield();

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"x.."+x);

? ? ? ? }

? ? }

3. Java实现线程程序

3.1 java.lang.Runnable接口

实现线程程序的步骤 :

定义类实现接口

重写接口的抽象方法run()

创建Thread类对象

Thread类构造方法中,传递Runnable接口的实现类对象

调用Thread对象方法start()启动线程

//- 定义类实现接口

// - 重写接口的抽象方法run()

public class SubRunnable implements Runnable{

? ? @Override

? ? public void run() {

? ? ? ? for(int x = 0 ; x < 50 ;x++){

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"x.."+x);

? ? ? ? }

? ? }

}

? ? public static void main(String[] args) {

? ? ? ? //创建接口实现类对象

? ? ? ? Runnable r = new SubRunnable();

? ? ? ? //创建Thread对象,构造方法传递接口实现类

? ? ? ? Thread t0 = new Thread(r);

? ? ? ? t0.start();

? ? ? ? for(int x = 0 ; x < 50 ;x++){

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"x.."+x);

? ? ? ? }

? ? }

3.2 实现接口的好处

接口实现好处是设计上的分离效果 : 线程要执行的任务和线程对象本身是分离的.

继承Thread重写方法run() : Thread是线程对象,run()是线程要执行的任务

实现Runnable接口 : 方法run在实现类,和线程无关,创建Thread类传递接口的实现类对象,线程的任务和Thread没有联系, 解开耦合性

4. 线程安全

出现线程安全的问题需要一个前提 : 多个线程同时操作同一个资源

线程执行调用方法run,同一个资源是堆内存的

4.1 售票例子

火车票的票源是固定的,购买渠道在火车站买,n多个窗口

/**

* 票源对象,需要多个线程同时操作

*/

public class Ticket implements Runnable {

? ? //定义票源

? ? private int tickets = 100;

? ? @Override

? ? public void run() {

? ? ? ? while (true) {

? ? ? ? ? ? if (tickets > 0) {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? Thread.sleep(10);//线程休眠,暂停执行

? ? ? ? ? ? ? ? }catch (Exception ex){}

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+" 出售第" + tickets + "张");

? ? ? ? ? ? ? ? tickets--;

? ? ? ? ? ? }else

? ? ? ? ? ? ? ? break;;

? ? ? ? }

? ? }

}

public static void main(String[] args) {

? ? Ticket ticket = new Ticket();

? ? //创建3个窗口,3个线程

? ? Thread t0 = new Thread(ticket);

? ? Thread t1 = new Thread(ticket);

? ? Thread t2 = new Thread(ticket);

? ? t0.start();

? ? t1.start();

? ? t2.start();

}

解决线程的安全问题 : 当一个线程没有完成全部操作的时候,其它线程不能操作

java多线程、线程同步与线程池,第2张

4.2 同步代码块

同步代码块可以解决线程安全问题 : 格式 synchronized关键字

synchronized(任意对象){

? ? //线程操作的共享资源

}

任意对象 : 在同步中这个对象称为对象锁,简称锁,官方的文档称为 对象监视器

同步代码块,如何保证线程的安全性.

同步代码块的执行原理 : 关键点就是对象锁

线程执行到同步,判断锁是否存在

如果锁存在,获取到锁,进入到同步中执行

执行完毕,线程出去同步代码块,讲锁对象归还

线程执行到同步,判断锁所否存在

如果锁不存在,线程只能在同步代码块这里等待,锁的到来

java多线程、线程同步与线程池,第3张

使用同步 : 线程要先判断锁,然后获取锁,出去同步要释放锁, 增加了许多步骤,因此线程安全运行速度慢. 牺牲性能,不能牺牲数据安全

4.3 同步方法

当一个方法中,所有代码都是线程操作的共享内容,可以在方法的定义上添加同步的关键字 synchronized , 同步的方法,或者称为同步的函数.

同步方法中有对象锁吗 , this对象

静态同步方法中有对象锁吗,锁对象是本类.class属性. 这个属性表示这个类的class文件的对象.

? ? @Override

? ? public void run() {

? ? ? ? while (true)

? ? ? ? ? sale();

? ? }

private static synchronized void sale(){

? ? //? synchronized (Ticket.class) {

? ? if (tickets > 0) {

? ? try {

? ? ? ? Thread.sleep(20);//线程休眠,暂停执行

? ? ? ? } catch (Exception ex) {

? ? }

? ? System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");

? ? tickets--;

? ? }

//? }

}

5. 死锁

死锁程序 : 多个线程同时争夺同一个锁资源,出现程序的假死现象.

面试点 : 考察开发人员是否充分理解同步代码的执行原理

同步代码块 : 线程判断锁,获取锁,释放锁,不出代码,锁不释放

完成死锁的案例 : 同步代码块的嵌套

java多线程、线程同步与线程池,第4张

死锁代码

/**

* 实现死锁程序

*/

public class ThreadDeadLock implements Runnable{

? ? private boolean flag ;

? ? public ThreadDeadLock(boolean flag){

? ? ? ? this.flag = flag;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true){

? ? ? ? ? ? //同步代码块的嵌套

? ? ? ? ? ? if (flag){

? ? ? ? ? ? ? ? //先进入A锁同步

? ? ? ? ? ? ? ? synchronized (LockA.lockA){

? ? ? ? ? ? ? ? ? ? System.out.println("线程获取A锁");

? ? ? ? ? ? ? ? ? ? //在进入另一个同步B锁

? ? ? ? ? ? ? ? ? ? synchronized (LockB.lockB){

? ? ? ? ? ? ? ? ? ? ? ? System.out.println("线程获取B锁");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }else {

? ? ? ? ? ? ? ? //先进入B锁同步

? ? ? ? ? ? ? ? synchronized (LockB.lockB){

? ? ? ? ? ? ? ? ? ? System.out.println("线程获取B锁");

? ? ? ? ? ? ? ? ? ? //再进入另一个同步锁A锁

? ? ? ? ? ? ? ? ? ? synchronized (LockA.lockA){

? ? ? ? ? ? ? ? ? ? ? ? System.out.println("线程获取A锁");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

public class LockA {

? ? public static LockA lockA = new LockA();

}

public class LockB {

? ? public static LockB lockB = new LockB();

}

? ? public static void main(String[] args) {

? ? ? ? ThreadDeadLock threadDeadLock = new ThreadDeadLock(true);

? ? ? ? ThreadDeadLock threadDeadLock2 = new ThreadDeadLock(false);

? ? ? ? new Thread(threadDeadLock).start();

? ? ? ? new Thread(threadDeadLock2).start();

? ? }

6. JDK5新特性Lock锁

JDK5新的特性 : java.util.concurrent.locks包. 定义了接口Lock.

Lock接口替代了synchronized,可以更加灵活

Lock接口的方法

void lock() 获取锁

void unlock()释放锁

Lock接口的实现类ReentrantLock

/**

*? 优化为juc包的接口Lock

*/

public class Ticket implements Runnable {

? ? //定义票源

? ? private? int tickets = 100;

? ? //获取Lock接口的实现类对象

? ? private Lock lock = new ReentrantLock();

? ? @Override

? ? public void run() {

? ? ? ? while (true)

? ? ? ? ? sale();

? ? }

? ? private void sale(){

? ? ? ? //获取锁

? ? ? ? lock.lock();

? ? ? ? if (tickets > 0) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? Thread.sleep(20);//线程休眠,暂停执行

? ? ? ? ? ? } catch (Exception ex) {

? ? ? ? ? ? }

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");

? ? ? ? ? ? tickets--;

? ? ? ? }

? ? ? ? //释放锁

? ? ? ? lock.unlock();

? ? }

}

7. 生产者与消费者

创建2个线程,一个线程表示生产者,另一个线程表示消费者

java多线程、线程同步与线程池,第5张

/**

* 定义资源对象

*? 成员 : 产生商品的计数器

*? ? ? ? ? 标志位

*/

public class Resource {

? ? int count ;

? ? boolean flag ;

}

/**

* 生产者线程

*? 资源对象中的变量++

*/

public class Produce implements Runnable{

? ? private Resource r ;

? ? public Produce(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true){

? ? ? ? ? ? synchronized (r) {

? ? ? ? ? ? ? ? //判断标志位,是否允许生产

? ? ? ? ? ? ? ? //flag是true,生产完成,等待消费

? ? ? ? ? ? ? ? if (r.flag )

? ? ? ? ? ? ? ? ? ? //无限等待

? ? ? ? ? ? ? ? ? try{ r.wait();

? ? ? ? ? ? ? ? ? }catch (Exception ex){}

? ? ? ? ? ? ? ? r.count++;

? ? ? ? ? ? ? ? System.out.println("生产第" + r.count + "个");

? ? ? ? ? ? ? ? //修改标志位,已经生产了,需要消费

? ? ? ? ? ? ? ? r.flag = true;

? ? ? ? ? ? ? ? //唤醒消费者线程

? ? ? ? ? ? ? ? r.notify();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

/**

* 消费者线程

*? 资源对象中的变量输出打印

*/

public class Customer implements Runnable{

? ? private Resource r ;

? ? public Customer(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true){

? ? ? ? ? ? synchronized (r) {

? ? ? ? ? ? ? ? //是否要消费,判断标志位 ,允许消费才能执行

? ? ? ? ? ? ? ? if (!r.flag )

? ? ? ? ? ? ? ? ? ? //消费完成,不能再次消费,等待生产

? ? ? ? ? ? ? ? ? ? try{r.wait();}catch (Exception ex){}

? ? ? ? ? ? ? ? System.out.println("消费第" + r.count);

? ? ? ? ? ? ? ? ? ? //消费完成后,修改标志位,变成已经消费

? ? ? ? ? ? ? ? r.flag = false;

? ? ? ? ? ? ? ? //唤醒生产线程

? ? ? ? ? ? ? ? r.notify();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

public static void main(String[] args) {

? ? Resource r = new Resource();

? ? //接口实现类,生产的,消费的

? ? Produce produce = new Produce(r);

? ? Customer customer = new Customer(r);

? ? //创建线程

? ? new Thread(produce).start();

? ? new Thread(customer).start();

}

线程通信的方法 wait() notify()

方法的调用必须写在同步中

调用者必须是作为锁的对象

wait(),notify()为什么要定义在Object类

同步中的锁,是任意对象,任何类都继承Object

案例改为方法实现

/**

* 定义资源对象

*? 成员 : 产生商品的计数器

*? ? ? ? ? 标志位

*/

public class Resource {

? private int count ;

? private boolean flag ;

? //消费者调用

? public synchronized void getCount() {

? ? ? ? //flag是false,消费完成,等待生产

? ? ? ? if (!flag)

? ? ? ? ? ? //无限等待

? ? ? ? ? ? try{this.wait();}catch (Exception ex){}

? ? ? ? System.out.println("消费第"+count);

? ? ? ? ? ? //修改标志位,为消费完成

? ? ? ? flag = false;

? ? ? ? //唤醒对方线程

? ? ? ? this.notify();

? }

? //生产者调用

? public synchronized void setCount() {

? ? ? ? //flag是true,生产完成,等待消费

? ? ? ? if (flag)

? ? ? ? ? ? //无限等待

? ? ? ? ? ? try{this.wait();}catch (Exception ex){}

? ? ? ? count++;

? ? ? ? System.out.println("生产第"+count+"个");

? ? ? ? //修改标志位,为生产完成

? ? ? ? flag = true;

? ? ? ? //唤醒对方线程

? ? ? ? this.notify();

? }

}

/**

* 消费者线程

*? 资源对象中的变量输出打印

*/

public class Customer implements Runnable{

? ? private Resource r ;

? ? public Customer(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true) {

? ? ? ? ? ? r.getCount();

? ? ? ? }

? ? }

}

/**

* 生产者线程

*? 资源对象中的变量++

*/

public class Produce implements Runnable{

? ? private Resource r ;

? ? public Produce(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true) {

? ? ? ? ? ? r.setCount();

? ? ? ? }

? ? }

}

//测试类

package thread.thread09;

public class ThreadTest {

? ? public static void main(String[] args) {

//? ? ? ? 接口实现类,生产者,消费者

? ? ? ? Resource r = new Resource();

? ? ? ? Produce produce = new Produce(r);

? ? ? ? Customer customer=new Customer(r);

//? ? 创建线程

? ? ? ? new Thread(produce).start();

//? ? ? ? Produce produce1=new Produce(r);

//? ? ? ? Thread t1=new Thread(produce1);

//? ? ? ? t1.start();跟上边效果相同

? ? ? ? new Thread(customer).start();

? ? }

}

7.1 安全问题产生

线程本身就是一个新创建的方法栈内存 (CPU进来读取数据)

线程的notify(),唤醒第一个等待的线程

解决办法 : 全部唤醒 notifyAll()

被唤醒线程,已经进行过if判断,一旦醒来继续执行

线程被唤醒后,不能立刻就执行,再次判断标志位,利用循环

while(标志位) 标志位是true,永远也出不去

/**

* 定义资源对象

*? 成员 : 产生商品的计数器

*? ? ? ? ? 标志位

*/

public class Resource {

? private int count ;

? private boolean flag ;

? //消费者调用

? public synchronized void getCount() {

? ? ? ? //flag是false,消费完成,等待生产

? ? ? ? while (!flag)

? ? ? ? ? ? //无限等待

? ? ? ? ? ? try{this.wait();}catch (Exception ex){}

? ? ? ? System.out.println("消费第"+count);

? ? ? ? ? ? //修改标志位,为消费完成

? ? ? ? flag = false;

? ? ? ? //唤醒对方线程

? ? ? ? this.notifyAll();

? }

? //生产者调用

? public synchronized void setCount() {

? ? ? ? //flag是true,生产完成,等待消费

? ? ? while (flag)

? ? ? ? ? ? //无限等待

? ? ? ? ? ? try{this.wait();}catch (Exception ex){}

? ? ? ? count++;

? ? ? ? System.out.println("生产第"+count+"个");

? ? ? ? //修改标志位,为生产完成

? ? ? ? flag = true;

? ? ? ? //唤醒对方线程

? ? ? ? this.notifyAll();

? }

}

/**

* 生产者线程

*? 资源对象中的变量++

*/

public class Produce implements Runnable{

? ? private Resource r ;

? ? public Produce(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true) {

? ? ? ? ? ? r.setCount();

? ? ? ? }

? ? }

}

/**

* 消费者线程

*? 资源对象中的变量输出打印

*/

public class Customer implements Runnable{

? ? private Resource r ;

? ? public Customer(Resource r) {

? ? ? ? this.r = r;

? ? }

? ? @Override

? ? public void run() {

? ? ? ? while (true) {

? ? ? ? ? ? r.getCount();

? ? ? ? }

? ? }

}

? ? public static void main(String[] args) {

? ? ? ? Resource r = new Resource();

? ? ? ? //接口实现类,生产的,消费的

? ? ? ? Produce produce = new Produce(r);

? ? ? ? Customer customer = new Customer(r);

? ? ? ? //创建线程

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(produce).start();

? ? ? ? new Thread(customer).start();

? ? ? ? new Thread(customer).start();

? ? ? ? new Thread(customer).start();

? ? ? ? new Thread(customer).start();

? ? ? ? new Thread(customer).start();

? ? ? ? new Thread(customer).start();

? ? }

java多线程、线程同步与线程池,第6张

7.2 线程方法sleep和wait的区别

sleep在休眠的过程中,同步锁不会丢失 ,不释放

wait()等待的时候,发布监视器的所属权, 释放锁.唤醒后要重新获取锁,才能执行

7.3 生产者和消费者案例性能问题

wait()方法和notify()方法, 本地方法调用OS的功能,和操作系统交互,JVM找OS,把线程停止. 频繁等待与唤醒,导致JVM和OS交互的次数过多.

notifyAll()唤醒全部的线程,也浪费线程资源,为了一个线程,不得以唤醒的了全部的线程.

7.4 Lock接口深入

Lock接口替换了同步synchronized, 提供了更加灵活,性能更好的锁定操作

Lock接口中方法 : newCondition() 方法的返回值是接口 : Condition

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PHlspTLg-1638013498872)(img/线程阻塞队列.JPG)]

7.5 生产者与消费者改进为Lock接口

Condition接口 (线程的阻塞队列)

进入队列的线程,释放锁

出去队列的线程,再次的获取锁

接口的方法 : await() 线程释放锁,进入队列

接口的方法 : signal() 线程出去队列,再次获取锁

线程的阻塞队列,依赖Lock接口创建

/**

*? 改进为高性能的Lock接口和线程的阻塞队列

*/

public class Resource {

? private int count ;

? private boolean flag ;

? private Lock lock = new ReentrantLock();//Lock接口实现类对象

? ? //Lock接口锁,创建出2个线程的阻塞队列

? ? private Condition prod = lock.newCondition();//生产者线程阻塞队列

? ? private Condition cust = lock.newCondition();//消费者线程阻塞队列

? //消费者调用

? public? void getCount() {

? ? ? lock.lock();//获取锁

? ? ? ? //flag是false,消费完成,等待生产

? ? ? ? while (!flag)

? ? ? ? ? ? //无限等待,消费线程等待,执行到这里的线程,释放锁,进入到消费者的阻塞队列

? ? ? ? ? ? try{cust.await();}catch (Exception ex){}

? ? ? ? System.out.println("消费第"+count);

? ? ? ? ? ? //修改标志位,为消费完成

? ? ? ? flag = false;

? ? ? ? //唤醒生产线程队列中的一个

? ? ? ? prod.signal();

? ? ? ? lock.unlock();//释放锁

? }

? //生产者调用

? public? void setCount() {

? ? ? lock.lock();//获取锁

? ? ? ? //flag是true,生产完成,等待消费

? ? ? while (flag)

? ? ? ? ? ? //无限等待,释放锁,进入到生产线程队列

? ? ? ? ? ? try{prod.await();}catch (Exception ex){}

? ? ? ? count++;

? ? ? ? System.out.println("生产第"+count+"个");

? ? ? ? //修改标志位,为生产完成

? ? ? ? flag = true;

? ? ? ? //唤醒消费者线程阻塞队列中年的一个

? ? ? ? cust.signal();

? ? ? lock.unlock();//释放锁

? }

}

测试类:

package thread.thread11;

public class test {

? ? public static void main(String[] args) {

? ? ? ? Res res = new Res();

? ? ? ? Pro pro = new Pro(res);

? ? ? ? Cust cust = new Cust(res);

? ? ? ? Thread t0 = new Thread(pro);

? ? ? ? t0.setName("生产者1号");

? ? ? ? Thread t1 = new Thread(pro);

? ? ? ? t1.setName("生产者2号");

? ? ? ? Thread t2 = new Thread(pro);

? ? ? ? t2.setName("生产者3号");

? ? ? ? Thread t3=new Thread(cust);

? ? ? ? t3.setName("消费者1号");

? ? ? ? Thread t4=new Thread(cust);

? ? ? ? t4.setName("消费者2号");

? ? ? ? Thread t5=new Thread(cust);

? ? ? ? t5.setName("消费者3号");

? ? ? ? t0.start();

? ? ? ? t1.start();

? ? ? ? t2.start();

? ? ? ? t3.start();

? ? ? ? t4.start();

? ? ? ? t5.start();

? ? }

}

7.6 Lock锁的实现原理

使用技术不开源,技术的名称叫做轻量级锁

使用的是CAS锁 (Compare And Swap) 自旋锁

JDK限制 : 当竞争的线程大于等于10,或者单个线程自旋超过10次的时候

JDK强制CAS锁取消.升级为重量级锁 (OS锁定CPU和内存的通信总线)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i7BL35SY-1638013498875)(img/CAS锁.JPG)]

8. 单例设计模式

设计模式 : 不是技术,是以前的人开发人员,为了解决某些问题实现的写代码的经验.

所有的设计模式核心的技术,就是面向对象.

Java的设计模式有23种,分为3个类别,创建型,行为型,功能型

8.1 单例模式

要求 : 保证一个类的对象在内存中的唯一性

实现步骤

私有修饰构造方法

自己创建自己的对象

方法get,返回本类对象

/**

* - 私有修饰构造方法

* - 自己创建自己的对象

* - 方法get,返回本类对象

*/

public class Single {

? ? private Single(){}

? ? ? ? //饿汉式

? ? private static Single s = new Single(); // 自己创建自己的对象

//? ? 方法get,返回本类对象

? ? public static Single getInstance(){

? ? ? ? return s;

? ? }

}

public static void main(String[] args) {

? ? ? ? //静态方法,获取Single类的对象

? ? ? ? Single instance = Single.getInstance();

? ? ? ? System.out.println("instance = " + instance);

}

实现步骤

私有修饰构造方法

创建本类的成员变量, 不new对象

方法get,返回本类对象

/**

* - 私有修饰构造方法

* - 创建本类的成员变量, 不new对象

* - 方法get,返回本类对象

*/

public class Single {

? ? private Single(){}

? ? ? ? //懒汉,对象的延迟加载

? ? private static Single s = null;

? ? public static Single getInstance(){

? ? ? ? //判断变量s,是null就创建

? ? ? ? if (s == null) {

? ? ? ? ? ? s = new Single();

? ? ? ? }

? ? ? ? return s;

? ? }

}

8.2 懒汉式的安全问题

一个线程判断完变量 s=null,还没有执行new对象,被另一个线程抢到CPU资源,同时有2个线程都进行判断变量,对象创建多次

? ? public static Single getInstance(){

? ? ? ? synchronized (Single.class) {

? ? ? ? ? ? //判断变量s,是null就创建

? ? ? ? ? ? if (s == null) {

? ? ? ? ? ? ? ? s = new Single();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return s;

? ? }

性能问题 : 第一个线程获取锁,创建对象,返回对象. 第二个线程调用方法的时候,变量s已经有对象了,根本就不需要在进同步,不要在判断空,直接return才是最高效的.双重的if判断,提高效率 Double Check Lock

private static volatile Single s = null;

public static Single getInstance(){

? ? ? ? //再次判断变量,提高效率

? ? ? ? if(s == null) {

? ? ? ? ? ? synchronized (Single.class) {

? ? ? ? ? ? ? ? //判断变量s,是null就创建

? ? ? ? ? ? ? ? if (s == null) {

? ? ? ? ? ? ? ? ? ? s = new Single();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return s;

? ? }

8.3 关键字volatile

成员变量修饰符,不能修饰其它内容

关键字作用 :

保证被修饰的变量,在线程中的可见性

防止指令重排序

单例的模式, 使用了关键字,不使用关键字,可能线程会拿到一个尚未初始化完成看的对象(半初始化)

public class MyRunnable implements Runnable {

? ? private volatile boolean flag = true;

? ? @Override

? ? public void run() {

? ? ? ? m();

? ? }

? ? private void m(){

? ? ? ? System.out.println("开始执行");

? ? ? ? while (flag){

? ? ? ? }

? ? ? ? System.out.println("结束执行");

? ? }

? ? public void setFlag(boolean flag) {

? ? ? ? this.flag = flag;

? ? }

}

? ? public static void main(String[] args) throws InterruptedException {

? ? ? ? MyRunnable myRunnable = new MyRunnable();

? ? ? ? new Thread(myRunnable).start();

? ? ? ? Thread.sleep(2000);

? ? ? ? //main线程修改变量

? ? ? ? myRunnable.setFlag(false);

? ? }

9. 线程池ThreadPool

线程的缓冲池,目的就是提高效率. new Thread().start() ,线程是内存中的一个独立的方法栈区,JVM没有能力开辟内存空间,和OS交互.

JDK5开始内置线程池

9.1 Executors类

静态方法static newFixedThreadPool(int 线程的个数)

方法的返回值ExecutorService接口的实现类,管理池子里面的线程

ExecutorService接口的方法

submit (Runnable r)提交线程执行的任务

9.2 Callable接口

实现多线程的程序 : 接口特点是有返回值,可以抛出异常 (Runnable没有)

抽象的方法只有一个, call

启动线程,线程调用重写方法call

ExecutorService接口的方法

submit (Callable c)提交线程执行的任务

Future submit()方法提交线程任务后,方法有个返回值 Future接口类型

Future接口,获取到线程执行后的返回值结果

public class MyCall implements Callable<String> {

? ? public String call() throws Exception{

? ? ? ? return "返回字符串";

? ? }

}

? ? public static void main(String[] args) throws ExecutionException, InterruptedException {

? ? ? ? //创建线程池,线程的个数是2个

? ? ? ExecutorService? es =? Executors.newFixedThreadPool(2);

? ? ? //线程池管理对象service,调用方法啊submit提交线程的任务

? ? ? ? MyRunnable my = new MyRunnable();

? ? ? ? //提交线程任务,使用Callable接口实现类

? ? ? ? Future<String> future = es.submit(new MyCall());//返回接口类型 Future

? ? ? ? //接口的方法get,获取线程的返回值

? ? ? ? String str = future.get();

? ? ? ? System.out.println("str = " + str);

//? ? ? ? es.submit(my);

//? ? ? ? es.submit(my);

//? ? ? ? es.submit(my);

? ? ? // es.shutdown();//销毁线程池

? ? }

10. ConcurrentHashMap

ConcurrentHashMap类本质上Map集合,键值对的集合.使用方式和HashMap没有区别.

凡是对于此Map集合的操作,不去修改里面的元素,不会锁定

11. 线程的状态图-生命周期

在某一个时刻,线程只能处于其中的一种状态. 这种线程的状态反应的是JVM中的线程状态和OS无关.


https://www.xamrdz.com/backend/3yt1940947.html

相关文章: