多线程下
单例设计模式:
保证类在内存中只有一个对象
如何保证类在内存中只有一个对象呢?
1、控制类的创建,不让其他类来创建本类的对象
2、在本类中定义一个本类的对象,Singleton s
3、提供公共的访问方式
单例写法两种:
1、饿汉式 开发用这种
2、懒汉式 面试用这种
3、第三种格式
public class Demo1 { /** * 单例设计模式:保证类在内存中只有一个对象 */ public static void main(String[] args) { //Singleton s1 = new Singleton(); Singleton s1 = Singleton.s; //成员变量被私有,不能通过类名.调用 //Singleton.s = null; Singleton s2 = Singleton.s; System.out.println(s1 == s2); /*Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2);*/ } } /** * 饿汉式 * @author clq * class Singleton { //1、私有构造方法,其他类不能访问该构造方法了 private Singleton(){ } //2、创建本类对象 private static Singleton s = new Singleton(); //3、对外提供公共的访问方法 public static Singleton getInstance(){ //获取实例 return s; } }*/ /** * 懒汉式,单例的延迟加载模式 * @author clq * */ /*class Singleton { //1、私有构造方法,其他类不能访问该构造方法了 private Singleton(){} //2、声明一个引用 private static Singleton s; //3、对外提供公共的访问方法 public static Singleton getInstance(){ //获取实例 if (s == null) { //线程1等待,线程2等待 s = new Singleton(); } return s; } }*/ /** * 饿汉式和懒汉式的区别: * 1、饿汉式是空间换时间,懒汉式是时间换空间 * 2、在多线程访问是,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象 * */ class Singleton { //1、私有构造方法,其他类不能访问该构造方法了 private Singleton(){} //2、声明一个引用 public static final Singleton s = new Singleton(); }
Runtime类:
Runtime类是一个单例类
import java.io.IOException; public class Demo2 { public static void main(String[] args) throws IOException { Runtime r = Runtime.getRuntime(); //获取运行时对象 //r.exec("shutdown -s -t 300"); r.exec("shutdown -a"); } }
Timer:
Timer类:计时器
import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class Demo3 { public static void main(String[] args) throws InterruptedException { Timer t = new Timer(); t.schedule(new MyTimerTask(), new Date(119,5,5,9,42,50),3000); //在指定时间安排指定任务 //第一个参数,是安排的任务,第二个参数是执行的时间,第三个参数是过多长时间再重复执行 while(true){ Thread.sleep(1000); System.out.println(new Date()); } } } class MyTimerTask extends TimerTask { @Override public void run() { System.out.println("我爱学习"); } }
两个线程间的通信:
1、什么时候需要通信
多个线程并发执行时,在默认情况下CPU是随机切换线程的
如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印
2、怎么通信
如果希望线程等待,就调用wait()
如果希望唤醒等待的线程,就调用notify()
这两个方法必须在同步的代码中执行,并且使用同步锁对象来调用
import java.util.FormatFlagsConversionMismatchException; public class Demo4 { public static void main(String[] args) { final Printer p = new Printer(); new Thread(){ public void run(){ while(true){ try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ public void run(){ while(true){ try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } //等待唤醒机制 class Printer { private int flag = 1; public void print1() throws InterruptedException { synchronized(this){ if (flag != 1) { this.wait(); //当前线程等待 } System.out.print("H"); System.out.print("e"); System.out.print("l"); System.out.print("l"); System.out.print("o"); System.out.print(" "); flag = 2; this.notify(); //随机唤醒单个等待的线程 } } public void print2() throws InterruptedException { synchronized (this) { if (flag != 2) { this.wait(); } System.out.print("酷"); System.out.print("狗"); System.out.print(" "); flag = 1; this.notify(); } } }
三个或三个以上间的线程通信:
多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件
public class Demo5 { public static void main(String[] args) { final Printer2 p = new Printer2(); new Thread() { public void run() { while (true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while (true) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer2 { private int flag = 1; public void print1() throws InterruptedException { synchronized (this) { while (flag != 1) { this.wait(); // 当前线程等待 } System.out.print("H"); System.out.print("e"); System.out.print("l"); System.out.print("l"); System.out.print("o"); System.out.print(" "); flag = 2; // this.notify(); //随机唤醒单个等待的线程 this.notifyAll(); } } public void print2() throws InterruptedException { synchronized (this) { while (flag != 2) { this.wait(); // 线程2在此等待 } System.out.print("酷"); System.out.print("狗"); System.out.print(" "); flag = 3; // this.notify(); this.notifyAll(); } } public void print3() throws InterruptedException { synchronized (this) { while (flag != 3) { this.wait(); // 线程3在此等待,if语句是在哪里等待,就在哪里起来 } // while循环是循环判断,每次都会判断标记 System.out.print("m"); System.out.print("u"); System.out.print("s"); System.out.print("i"); System.out.print("c"); System.out.print(" "); flag = 1; // this.notify(); this.notifyAll(); } } }
线程间的通信注意的问题:
1、在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
2、为什么wait方法和notify方法定义在Object这类中?
因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中
3、sleep方法和wait方法的区别?
a:sleep方法必须传入参数,参数就是时间,时间到了自动醒来
wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
b:sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡
wait方法在同步函数或者同步代码块中,释放锁
JDK1.5的新特性互斥锁:
1、同步
使用ReentrantLock类的lock()和unlock()方法进行同步
2、通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法
不同的线程使用不同的Condition,这样就能区分唤醒的时候找哪个线程了
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Demo6 { public static void main(String[] args) { Printer3 p = new Printer3(); new Thread(){ public void run(){ while(true){ try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ public void run(){ while(true){ try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread(){ public void run(){ while(true){ try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer3 { private ReentrantLock r = new ReentrantLock(); private Condition c1 = r.newCondition(); private Condition c2 = r.newCondition(); private Condition c3 = r.newCondition(); private int flag = 1; public void print1() throws InterruptedException { r.lock(); //获取锁 if (flag != 1) { c1.await(); } System.out.print("H"); System.out.print("e"); System.out.print("l"); System.out.print("l"); System.out.print("o"); System.out.print(" "); flag = 2; // this.notify(); //随机唤醒单个等待的线程 c2.signal(); r.unlock(); //释放锁 } public void print2() throws InterruptedException { r.lock(); if (flag != 2) { c2.await(); } System.out.print("酷"); System.out.print("狗"); System.out.print(" "); flag = 3; // this.notify(); c3.signal(); r.unlock(); } public void print3() throws InterruptedException { r.lock(); if (flag != 3) { c3.await(); } System.out.print("m"); System.out.print("u"); System.out.print("s"); System.out.print("i"); System.out.print("c"); System.out.print(" "); flag = 1; // this.notify(); c1.signal(); r.unlock(); } }
线程的五种状态:
新建,就绪,运行,阻塞,死亡
GUI
如何创建一个窗口并显示:
Graphical User Interface(图形用户接口)
package com.gui; import java.awt.Frame; import java.awt.Toolkit; public class Demo1 { public static void main(String[] args) { Frame f = new Frame("我的第一个窗口"); f.setSize(400,600); //设置窗体大小 f.setLocation(500, 50); //设置窗体位置 f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png")); f.setVisible(true); //设置窗体可见 } }
布局管理器:
FlowLayout(流式布局管理器)
从左到右的顺序排列
Panel默认的布局管理器
BorderLayout(边界布局管理器)
东,南,西,北,中
Frame默认的布局管理器
GridLayout(网格布局管理器)
规则的矩阵
CardLayout(卡片布局管理器)
选项卡
GridBahLayout(网格包布局管理器)
非规则的矩阵
package com.gui; import java.awt.Button; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Toolkit; public class Demo1 { public static void main(String[] args) { Frame f = new Frame("我的第一个窗口"); f.setSize(400,600); //设置窗体大小 f.setLocation(500, 50); //设置窗体位置 f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png")); Button b1 = new Button("按钮"); f.add(b1); f.setLayout(new FlowLayout()); //设置布局管理器 f.setVisible(true); //设置窗体可见 } }
窗体监听:
package com.gui; import java.awt.Button; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; public class Demo1 { public static void main(String[] args) { Frame f = new Frame("我的第一个窗口"); f.setSize(400, 600); // 设置窗体大小 f.setLocation(500, 50); // 设置窗体位置 f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png")); Button b1 = new Button("按钮"); f.add(b1); f.setLayout(new FlowLayout()); // 设置布局管理器 // f.addWindowListener(new MyWindowAdapter()); f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); f.setVisible(true); // 设置窗体可见 } }
鼠标监听:
b1.addMouseListener(new MouseAdapter() { /*@Override public void mouseClicked(MouseEvent e) { //单击 System.exit(0); }*/ @Override public void mouseReleased(MouseEvent e) { //释放 System.exit(0); } });
键盘监听和键盘事件:
b1.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { //System.exit(0); //System.out.println(e.getKeyCode()); //if (e.getKeyCode() == 32) { if (e.getKeyCode() == KeyEvent.VK_SPACE) { System.exit(0); } } });
动作监听:
b2.addActionListener(new ActionListener() { //添加动作监听,应用场景就是暂停视频和播放视频 @Override public void actionPerformed(ActionEvent e) { System.exit(0); } });
适配器设计模式:
a:什么是适配器
在使用监听器的时候,需要定义一个类事件监听器接口
通常接口中有多个方法,而程序中不一定所有的都用到,但又必须重写,这很繁琐
适配器简化了这些操作,我们定义监听器时只要继承适配器,然后重写需要的方法即可
b:适配器原理
适配器就是一个类,实现了监听器接口,所有抽象方法都重写了,但是方法全是空的
适配器类需要定义成抽象的,因为创建该类对象,调用空方法是没有意义的
目的就是为了简化程序员的操作,定义监听器时继承适配器,只重写需要的方法就可以了
事件处理:
事件:用户的一个操作
事件源:被操作的组件
监听器:一个自定义类的对象,实现了监听器接口,包含事件处理方法,吧监听器添加在事件源上,
当事件发生的时候虚拟机就会自动调用监听器中的事件处理方法