一.线程方法
.wait方法
线程使用wait()方法后,进入等待状态,然后会将锁还回去,其他的线程才能进来。
线程被唤醒后,线程是从等待的位置继续向下开始执行。
notify方法
notify() :在同一把锁下 ,等待中的线程中,随机唤醒一个。
notifyAll() :唤醒同一把锁下 所有的等待线程。
notify和wait方法应用:(wait方法需要声明标记控制等待线程被唤醒)
/*
需求:
* Person类 姓名 性别
* 开启两个线程
* 一个对Person对象进行赋值
* 一个对Person对象进行打印
* 要求
* 一次打印 东东 男
* 一次打印 dongdong nv
* 间隔输出
*/
public class Kll {
public static void main(String[] args) {
// 创建共享Person
Person1 person = new Person1();
// 创建赋值和打印线程
CopyThread1 copyThread = new CopyThread1(person);
Thread t1= new Thread(copyThread);
PrintThread1 printThread = new PrintThread1(person);
Thread t2 = new Thread(printThread);
// 开启线程
t1.start();
t2.start();
}
}
class Person1 {
public String name;
public String gander;
// 声明标记,控制等待线程唤醒
public boolean flag = true;
// 封装同步代码块
// 赋值
public synchronized void setPerson(String name, String gander) {
// 判断等待
if (flag == false) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 赋值
this.name = name;
this.gander = gander;
// 唤醒
this.flag = false;
this.notify();
}
// 打印
public synchronized void printPerson() {
if (flag == true) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(name + "--" + gander);
// 唤醒
this.flag = true;
this.notify();
}
}
// 赋值线程
// 注意:赋值和打印要使用同一个对象
// 只创建一个对象,把person对象,当做参数传入线程
// 避免赋值线程还没完成赋值,就被打印线程打印了的问题;需要给一个锁
// 使用什么锁合适?
// 保证同一把锁 直接使用person当锁对象
// 如何实现间隔打印?
// 等赋值完成了,再进行打印 wait() 和 notify()
// 使用标记来控制等待和唤醒 保证两个线程使用的同一个标记
// 直接声明在Person类中
// 能不能封装成同步方法?写在哪儿?
// 能不能直接写在Person类中,同步成set/get方法
class CopyThread1 implements Runnable{
// 声明标记,用于间隔赋值
private boolean isTrue = false;
// 将Person对象声明为一个成员变量
private Person1 p;
// 通过构造方法给p对象赋值
public CopyThread1() {
}
public CopyThread1(Person1 p) {
this.p = p;
}
@Override
public void run() {
while (true) {
if (isTrue) {
p.setPerson("东东", "男");
}else {
p.setPerson("dongdong", "nv");
}
isTrue = !isTrue;
}
}
}
// 打印线程
class PrintThread1 implements Runnable{
// 将Person对象声明为一个成员变量
private Person1 p;
// 通过构造方法给p对象赋值
public PrintThread1() {
}
public PrintThread1(Person1 p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.printPerson();
}
}
}
join方法
插队方法:获取CPU的执行资源(先执行调用了join方法的线程)。
setDaemon方法
守护线程(当所有线程都结束了,这个线程就会结束)。
参数是:设置是否守护线程。
注意:要线程启动前调用
notify方法和setDaemon方法的应用代码:
需求:/*
* 创建两个子线程
* 第一个子线程循环打印10次名字
* 第二个死循环打印线程名字
* 主线程 循环10次名字
* 要求
* 等第一个子线程打印完 ,主线程再打印,主线程结束后,第二个子线程结束打印
*/
public class Kll {
public static void main(String[] args) {
// 守护线程(当所有线程都结束了,这个线程就会结束)
TestARun testARun = new TestARun();
Thread t2 = new Thread(testARun);
// 设置是否守护线程
// 注意:要线程启动前调用
t2.setDaemon(true);
t2.start();
TestRun testRun = new TestRun();
Thread t1 = new Thread(testRun);
t1.start();
// 插队方法
// 获取CPU的执行资源(先执行调用了join方法的线程)
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 主线程打印10遍名字
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
System.out.println("主线程结束了");
}
}
// 第一个线程循环打印10次名字
class TestRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
// 第二个线程死循环打印名字
class TestARun implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("测试");
}
}
}
二.同步代码块的弊端与好处
- 好处:保证了共享数据(代码)安全。
- 弊端:线程在进入和出去代码块时,增加了拿锁和还锁的操作,比原来更耗费系统资源。
弊端:不管这个对象被创建出来没有,都会进入同步代码块(即拿锁和还锁的操作)。
提高效率:(加入判断)如果已经创建出来一次,下次就不让线程再进入同步代码块。
三.接口回调
案例
- 接口回调(调用接口中方法)
- 键盘输入1或2
- 1.红色打印
- 2.黑色打印
- 打印内容"接口回调"
代码:
public class Kll {
public static void main(String[] args) {
System.out.println("请输入1或2:");
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
PrintInterA pA = null;
if (str.equals("1")) {
pA = new RedPrint();
}else {
pA = new BlackPrint();
}
// 调用功能类方法
Print.printString(pA);
}
}
// 接口 打印方法
interface PrintInterA {
public abstract void print(String string);
}
// 功能类 打印字符串的方法
// 方法中使用接口方做参数
class Print {
public static void printString(PrintInterA pA) {
// 调用接口中的抽象方法
// 调用的是实现类传进来的实现类的方法
// 参数传给了实现类的string
pA.print("接口回调");
}
}
// 接口实现类
// 红色打印
class RedPrint implements PrintInterA{
@Override
public void print(String string) {
System.err.println(string);
}
}
// 黑色打印
class BlackPrint implements PrintInterA{
@Override
public void print(String string) {
System.out.println(string);
}
}
案例需求:
- 利用接口回调,完成下列操作
- 主线程处理任务
- 子线程去读文件,并且将文件(Kll1.java)打印在控制台
Kll1类代码:
package kll.hd;
public class Kll1 {
public static void main(String[] args) {
System.out.println("逻辑任务一");
System.out.println("逻辑任务二");
//调用功能类中的方法 开子线程读文件
ReadFile.readFile(new ReadFileInter() {
@Override
public void readString(String string) {
// 参数string 就是文件读取的字符串
// 刷新界面(就是打印一下)
System.out.println(string);
}
});
System.out.println("逻辑任务三");
System.out.println("逻辑任务四");
}
}
接口代码:
package kll.hd;
// 读取字符串
public interface ReadFileInter {
public abstract void readString(String string);
}
功能类
package kll.hd;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
// 功能类,参数时是接口
// 用户看的见的,都在主线程操作
// 用户看不见的,耗时的都要在子线程去操作
public class ReadFile {
public static void readFile(ReadFileInter inter) {
// 开个子线程去读文件
new Thread(new Runnable() {
@Override
public void run() {
// 读文件 相对路径即可
File file = new File("src/kll/hd/Demo02.java");
BufferedReader br = null;
// 拼接字符串用
StringBuffer sb = new StringBuffer();
try {
FileReader fr = new FileReader(file);
br = new BufferedReader(fr);
String string = null;
while ((string = br.readLine()) != null) {
// 拼接字符串
sb.append(string);
sb.append("\n");
}
// 利用方法中传进来的参数
// 代用接口中的方法
inter.readString(sb.toString());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}).start();;
}
}
四.完整版的懒汉式单例
class Lazy{
private static Lazy lazy = null;
private Lazy() {
}
public static Lazy getLazy() {
// 休眠一秒(扩大问题)
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 弊端:不管这个对象被创建出来没有,都会进入同步代码块(即拿锁和还锁的操作)
// 提高效率,如果已经创建出来一次
// 下次就不让线程再进入同步代码块
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy();
}
}
}
return lazy;
}
}