网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
* JDK5之前无法唤醒指定的一个线程
* 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, while来反复判断条件。
public class Demo02_MultiThread { /* * 三个线程的通信 * notifyAll():唤醒在此对象监视器上等待的所有线程。 */ public static void main(String[] args) { final Printer pt = new Printer(); new Thread() { public void run() { while (true) { try { pt.print01(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { pt.print02(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { pt.print03(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer { private int flag = 1; public void print01() throws InterruptedException { synchronized (this) { while (flag != 1) { this.wait(); } System.out.print(“1…坚”); System.out.print(“持”); System.out.print(“努”); System.out.print(“力”); System.out.print(“吧”); System.out.print(“\r\n”); flag = 2; this.notifyAll(); } } public void print02() throws InterruptedException { synchronized (this) { while (flag != 2) { this.wait(); } System.out.print(“2…J”); System.out.print(“U”); System.out.print(“S”); System.out.print(“T”); System.out.print(“\r\n”); flag = 3; this.notifyAll(); } } public void print03() throws InterruptedException { synchronized (this) { while (flag != 3) { this.wait(); } System.out.print(“3…P”); System.out.print(“e”); System.out.print(“a”); System.out.print(“c”); System.out.print(“e”); System.out.print(“\r\n”); flag = 1; this.notifyAll(); } } } |
1.4 多个线程之间的通信(JDk1.5新特性):
1.同步
* 使用ReentrantLock类的lock()和unlock()方法进行同步
一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
public Condition newCondition():返回用来与此 Lock 实例一起使用的 Condition 实例。
Condition接口:
await():造成当前线程在接到信号或被中断之前一直处于等待状态。
signal():唤醒一个等待线程。
2.通信
* 使用ReentrantLock类的newCondition()方法可以获取Condition对象
* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
public class Demo02_MultiThread { /* * 三个线程的通信 * ReentrantLock/Condition,这是JDK1.5之后版本的新特新 */ public static void main(String[] args) { final Printer3 pt = new Printer3(); new Thread() { public void run() { while (true) { try { pt.print01(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { pt.print02(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { pt.print03(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer3 { private ReentrantLock reentrantLock = new ReentrantLock(); // 创建一个互斥锁对象 private Condition condition1 = reentrantLock.newCondition(); // 获取的一个Condition对象 private Condition condition2 = reentrantLock.newCondition(); // 获取的一个Condition对象 private Condition condition3 = reentrantLock.newCondition(); // 获取的一个Condition对象 private int flag = 1; public void print01() throws InterruptedException { reentrantLock.lock(); // 获取锁 if (flag != 1) { condition1.await(); // 等待 } System.out.print(“1…坚”); System.out.print(“持”); System.out.print(“努”); System.out.print(“力”); System.out.print(“吧”); System.out.print(“\r\n”); flag = 2; condition2.signal(); // 唤醒 reentrantLock.unlock(); // 释放锁 } public void print02() throws InterruptedException { reentrantLock.lock(); if (flag != 2) { condition2.await(); } System.out.print(“2…J”); System.out.print(“U”); System.out.print(“S”); System.out.print(“T”); System.out.print(“\r\n”); flag = 3; condition3.signal(); reentrantLock.unlock(); } public void print03() throws InterruptedException { reentrantLock.lock(); if (flag != 3) { condition3.await(); } System.out.print(“3…P”); System.out.print(“e”); System.out.print(“a”); System.out.print(“c”); System.out.print(“e”); System.out.print(“\r\n”); flag = 1; condition1.signal(); reentrantLock.unlock(); } } |
二、多线程的高级应用:
2.1 线程组:
public class Demo04_ThreadGroup { /** * ThreadGroup线程组 */ public static void main(String[] args) { // demo01(); ThreadGroup group = new ThreadGroup(“我是一个WeChat线程组”); MyRun mr = new MyRun(); Thread thread1 = new Thread(group, mr, “WeChat-张三”); Thread thread2 = new Thread(group, mr, “WeChat-李四”); group.setDaemon(true); // 把整个线程组设置为守护线程 System.out.println(thread1.getThreadGroup().getName()); // 获取线程thread1所在线程组的名字 System.out.println(thread2.getThreadGroup().getName()); // 获取线程thread2所在线程组的名字 } private static void demo01() { MyRun mr = new MyRun(); Thread thread1 = new Thread(mr, “WeChat-张三”); Thread thread2 = new Thread(mr, “WeChat-李四”); // 直接放在主线程中的线程,默认会分在main线程组中 ThreadGroup group1 = thread1.getThreadGroup(); ThreadGroup group2 = thread2.getThreadGroup(); System.out.println(group1.getName()); System.out.println(group2.getName()); } } class MyRun implements Runnable { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + “---------” + i); } } } |
2.2 线程池:
程序启动一个新的线程成本比较高的,因为它涉及到与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是程序中需要创建大量的生存期短线程的时候,更应该考虑使用线程池。线程池中的线程在程序代码结束之后,并不会死亡,而是再次回到线程池,称为空闲状态,等待下一个对象来使用。JDK1.5版本起不需要手动写,由Java内置支持。
public class Demo05_Executor { /** * 线程池 */ public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(2); // 创建线程池 // 将线程放入线程池,会启动线程池 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.shutdown(); // 关闭线程池 } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + “---------” + i); } } } |
2.3 多线程的实现方式(实现Callable接口):
public class Demo06_Callable { /** * Callable接口——线程的第三种实现方式 */ public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService pool = Executors.newFixedThreadPool(3); // 创建一个线程池 Future future1 = pool.submit(new MyCallable(10)); // 将线程放进线程池中 Future future2 = pool.submit(new MyCallable(40)); // 将线程放进线程池中 Future future3 = pool.submit(new MyCallable(100)); // 将线程放进线程池中 System.out.println(future1.get()); System.out.println(future2.get()); System.out.println(future3.get()); pool.shutdown(); } } class MyCallable implements Callable { private int number; public MyCallable() { } public MyCallable(int number) { this.number = number; } @Override public Integer call() throws Exception { // 求1到指定整数之间的全部整数之和 int sum = 0; for (int i = 1; i <= number; i++) { sum += i; } return sum; } } |
三、常见设计模式:
3.1单例设计模式:
3.1.1 概述:
单例设计模式:保证类在内存中只有一个实例对象。比如BBS论坛中访客(当前在线人数)的实时统计,必须保证存储总数的对象只能是一个。
如何保证类在内存中只有一个对象?
控制类的创建,不让其他的类来创建本类对象——使用private修饰构造方法。
在本类中定义一个本类的对象。
对外提供一个公共的访问方法。
3.1.2 实现方式:
public class Demo01_Singleton { /** * 单例模式的两种实现:饿汉和懒汉 */ public static void main(String[] args) { Visitor visitor1 = Visitor.getInstance(); Visitor visitor2 = Visitor.getInstance(); System.out.println(visitor1 == visitor2); } } /* * 饿汉模式和懒汉模式的区别: * 1、饿汉模式是以空间换时间,懒汉模式是以时间换空间 * 2、在多线程访问时,饿汉模式一定不会创建多个对象的;懒汉模式有可能会创建出多个对象 * 3、开发时一般是使用饿汉模式,面试笔试考懒汉 */ class Visitor { // 懒汉模式 // 1.私有的构造方法,其他的类不能访问该构造方法 private Visitor() { } // 2.声明一个引用 private static Visitor visitor; // 3.对外提供公共的访问方法 public static Visitor getInstance() { // 获取实例 if (visitor == null) { visitor = new Visitor(); // 线程1等待,线程2会创建一个成功了 } return visitor; } } // class Visitor { // 饿汉模式 // // 1.私有的构造方法,其他的类不能访问该构造方法 // private Visitor() { // } // // // 2.创建一个本类对象 // private static Visitor visitor = new Visitor(); // // // 3.对外提供公共的访问方法 // public static Visitor getInstance() { // 获取实例 // return visitor; // } // } |
3.1.3 Runtime类:
Ctrl+Shift+T:查看源码。
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时对象。应用程序不能创建自己的 Runtime 类实例。
public class Demo02_Runtime { /** * Runtime类的定义采用饿汉模式,与当前的运行环境相连接 */ public static void main(String[] args) throws IOException { Runtime time = Runtime.getRuntime(); // 获取运行时对象 time.exec(“shutdown -s -t 300”); // 300s之后立即关机 time.exec(“shutdown -a”); // 中止系统关机 } } |
3.1.4 定时器类:
public class Demo03_Timer { /** * 单例模式实现的定时器 */ public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); // 创建一个定时器对象 // 在指定时间安排指定任务 // Date的构造器中: // year - 减 1900 的年份。 // month - 0-11 之间的月份。 // date - 一月中 1-31 之间的某一天。 // hrs - 0-23 之间的小时数。 // min - 0-59 之间的分钟数。 // sec - 0-59 之间的秒数。 // 第一参数是安排的任务,第二个参数是执行的时间,第三个参数是过多久之后再重复执行 timer.schedule(new MyTimerTask(), new |
3.2 简单工厂设计模式:
简单工厂设计模式,又被称为静态工厂方法模式,它定义一个具体的工厂类负责创建一些实例对象。
优点:客户端不需要负责对象的创建,从而明确了各个类的职责。
缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要修改工厂类,不利于后期的维护。
3.3 工厂方法模式:
工厂方法模式中,抽象工厂类负责定义创建对象的接口,具体的对象创建工作由继承抽象工厂的具体类实现。
优点:客户端不需要负责对象的创建,从而明确了各个类的职责;如果有新的对象的增加,只需要增加一个具体的类和具体的工厂类即可,不影响原有的代码,后期维护容易,增强了系统的扩展性。
缺点:需要额外的编写代码,增加了工作量。
Struts2
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新