一.wait和notify
wait是让线程进入等待状态
notify是随即唤醒一个等待中的线程并释放锁(在执行完整个同步代码块后)
notifyAll是唤醒所有等待的线程
example:
开启两个线程A和B 先打印五遍线程B再打印五遍线程A
class ABRunnable implements Runnable {
//声明一个标记来解决A先进来的问题
private boolean flag = false;
// 声明一把锁
private Object object = new Object();
@Override
public void run() {
// 循环打印AB 并打印线程名字
String name = Thread.currentThread().getName();
System.out.println(name + "进来了");
//加同步锁
//运行结果很随机 有时会卡住
//A先进来会卡住 B先进来运行正确
synchronized (object) {
if (name.equals("线程B") && !flag) {
//进入等待
try {
//如果B线程等待时 还拿着锁的话 会有什么现象
//当线程进入等待的时候会释放锁
//在哪个位置等待的 就从哪个位置被唤醒
object.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println(name);
}
}
for (int i = 0; i < 5; i++) {
System.out.println(name);
}
//如果线程A打印完了 线程B就不需要再等待了 直接打印就行
if (name.equals("线程A")) {
flag = true;
}
object.notify();
}
}
}
创建线程并开启
public static void main(String[] args) {
ABRunnable runnable = new ABRunnable();
Thread t1 = new Thread(runnable, "线程A");
Thread t2 = new Thread(runnable, "线程B");
t1.start();
t2.start();
}
单例饿汉式:如何保证多个线程调用时的安全性以及如何提高效率
class HungryMan {
// 延迟加载(等调用方法的时候在创建对象)
private static HungryMan hungryMan = null;
// 私有化构造方法(不让外界创建对象 对象由自己创建)
private HungryMan() {
// TODO Auto-generated constructor stub
}
public static HungryMan getInstance() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 在多线程程序中 这个单例安全吗 为什么
// 例如多个线程 调用该方法
// 加锁来保证同一时间只有一个线程来创建对象
//提高效率
if (hungryMan == null) {
synchronized (HungryMan.class) {
hungryMan = new HungryMan();
}
}
return hungryMan;
}
}
class HungryRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
HungryMan hungryMan = HungryMan.getInstance();
System.out.println(hungryMan);
}
}
}
join方法: 哪个线程调用了这个方法 就会拿到CPU的执行权 先完成执行
setDeamon: (守护线程)该方法必须在线程启动前调用 在主线程结束时结束
(并不是立即结束 需要一个缓冲时间)
example:
开启子线程循环打印十次 主线程循环打印十次 先打印子线程再打印主线程
class SonRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}class TestRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
System.out.println("测试");
}
主线程:
public static void main(String[] args) {
TestRunnable testRunnable = new TestRunnable();
Thread t2 = new Thread(testRunnable);
//该方法必须在线程启动前调用
t2.setDaemon(true);
t2.start();
SonRunnable sr = new SonRunnable();
Thread t1 = new Thread(sr);
t1.start();
//哪个线程调用了这个方法 就会拿到CPU的执行权
//先完成执行
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
System.out.println("主线程结束了");
}
二.接口回调
写法:
1.创建一个接口
2.写一个功能类(打印字符串的方法的参数 可以使用借口来接收 达到调用接口中方法的目的)
3.写实现类
example:
键盘输入1或2 输入1的时候打印红色的字 输入2的时候打印黑色的字
System.err.println("哈哈"); 打印的是红色的
System.out.println("呵呵"); 打印的是黑色的
创建接口:
//接口
interface PrintInter{
//打印方法
public abstract void print(String string);
}
class PrintClass{
//打印方法(接收接口的参数)
public static void printString(PrintInter inter) {
//调用接口中的方法
inter.print("接口回调");
}
}
写实现类:
class PrintRed implements PrintInter{
@Override
public void print(String string) {
System.err.println(string);
}
}
class PrintBlack implements PrintInter{
@Override
public void print(String string) {
System.out.println(string);
}
}
或者直接在main函数中写匿名内部类:
public static void main(String[] args) {
System.out.println("请输入 1 或 2 ");
Scanner scanner = new Scanner(System.in);
String string = scanner.nextLine();
PrintInter inter = null;
if (string.equals("1")) {
PrintClass.printString(new PrintInter() {
@Override
public void print(String string) {
System.err.println(string);
}
});
}else {
PrintClass.printString(new PrintInter() {
@Override
public void print(String string) {
System.out.println(string);
}
});
}
//调用功能类方法
PrintClass.printString(inter);
}