package ThreadDemo;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
* 线程Thread
*
* 1.了解
* 线程Thread: 进程中的小任务,多线程
* 进程process:计算机中在执行的任务,在CPU上执行和计算
* 注意:一个核上往往只能执行一个进程中的一个线程,
* 但计算机看起来像是运行多个进程,实际上是因为计算机任务切换速度非常快,超过人的反应。
*
* 进程的执行在宏观上并行,在微观上串行。
*
*
*
* 2.定义:
*
* 1第一种定义
* 1.1继承线程父类 extends Thread
* 1.2线程需要执行的逻辑需要放到run方法
* 1.3调用start方法来启动线程执行任务
*
* 考虑一个子类有父类,已继承,不能再继承Thread ,可以实现接口
* 2第二种定义(import)
* 2.1实现Runnable接口 implements Runnable
* 2.2线程需要执行的逻辑需要放到run方法
* 2.3创建Runnable对象,然后将Runnable对象作为参数传递到Thread对象中,利用Thread对象.start()来启动线程
*
*
* 考虑线程返回结果
* 3第三种定义
* 3.1实现Callable接口 implements Callable
* 3.2线程需要执行的逻辑需要放到call()方法
* 3.3执行线程
*
*/
public class ThreadDemo01Define {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//--------------------1.extends Thread-------------------------------
//1.3调用start方法来启动线程执行任务
ThreadDefine01 t1=new ThreadDefine01(); //创建一个线程对象
t1.start(); //让这个线程启动来执行任务
//注意 Thread.start()每次执行结果都不一样,而Thread.run()根据定义的顺序,先执行线程再执行主函数
// Thread.start()启动不一样的任务 Thread.run()认为是调用方法,与主函数一个任务
// t1.run();
for(int i=0;i<9;i++) {
System.out.println("主函数"+i);
}
//-------------------2.implements Runable------------------------------------------
//2.3执行线程
ThreadDefine02 t2=new ThreadDefine02(); //创建一个线程对象
//分配新的 Thread 对象。Thread(Runnable) 把Runable对象放入Thread()实现,启动Thread.start();
Thread t3=new Thread(t2);
t3.start();
//简写 new Thread(t2).start();
//2.2 匿名内部类
Runnable r=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<9;i++) {
System.out.println("主函数"+i);
}
}
};
new Thread(r).start();
//2.2匿名内部类Lamda表达式
Runnable r1=()->System.out.println("Hello");
new Thread(r1).start();
//2.2匿名内部类化简 (函数式编程)
new Thread(()->System.out.println("Hello")).start();
//---------------------3.implements Callable<E>--------------------------------
//3.3启动线程
ThreadDefine03 t4=new ThreadDefine03();
// 执行器服务 执行器工具类 缓存线程池
ExecutorService es = Executors.newCachedThreadPool();
//保存结果 执行线程
Future<String> f = es.submit(t4);
//关闭线程.shutdown
es.shutdown();
//获取线程执行结果.get()
System.out.println(f.get());
}
}
//1.定义线程
//1.1继承线程父类 extends Thread
class ThreadDefine01 extends Thread{
//1.2线程需要执行的逻辑需要放到run方法
@Override
public void run() {
for(int i=0;i<9;i++) {
System.out.println("extends Thread "+i);
}
}
}
//考虑一个子类有父类,已继承,不能再继承Thread ,可以实现接口
//2.定义线程
//2.1实现Runable接口 implements Runnable
class ThreadDefine02 implements Runnable{
//2.2线程需要执行的逻辑需要放到run方法
@Override
public void run() {
for(int i=0;i<9;i++) {
System.out.println("implements Runable"+i);
}
}
}
//考虑线程返回结果
//3.定义线程
//3.1实现Callable接口 implements Callable
//需要返回结果类型
class ThreadDefine03 implements Callable<String>{
//3.2线程需要执行的逻辑需要放到call()方法
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "sadadada";
}
}
package ThreadDemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
/*
* 1.多线程的并发安全问题
*
* 1.1多线程的并发安全问题:多个线程同时执行,而多个线程在执行的时候是相互抢占资源导致出现了不合理的数据的现象
* 多线程在执行的时候是相互抢占,而且抢占是发生在线程执行的每一步过程中
*
*
*
* 2.同步锁机制synchronized
*
* 2.1利用synchronized--同步代码块解决多线程并发安全问题
* 同步:一段逻辑在同一时间只能有一个线程执行
* 异步: 一段逻辑在同一时间能有多个线程执行
*
*
* 2.2同步一定是安全的吗? ---是
* 安全一定是同步的吗? --不是
*
* 异步不一定不安全
* 不安全一定是异步。
*
* 异步式线程安全 ConcurrentHashMap,只锁一个桶,不锁整个集合,是异步安全
* 从微观上而言,同步一定是安全的,安全也一定是同步的。
* 从宏观上而言,同步一定是安全的,安全不一定是同步的。
*
*
* 2.3需要一个锁对象,需要所有线程都认识这个锁对象,
* 锁对象有:共享对象,类的字节码(方法区的类的是被所有线程共享的),this(必须要求同一个对象 n个*new Thread(一直是s).start()这样开多个就能用this作为锁对象)
*
*
* 用synchronized修饰方法也是非静态, 锁对象:this
* 用synchronized修饰方法但是静态static,锁对象:类
*
*
* 2.4扩展:堆内存,方法区是被线程共享的,但是堆内存不是唯一的不能作为锁对象,方法区是唯一的能作为锁对象
* 栈内存,本地方法栈,PC计数器是是每一个线程所独有的。
*
*
* 2.5死锁DeadLock:由于锁直接相互嵌套并且锁对象不同导致线程之间相互锁死,致使代码无法执行
*
* 避免死锁:统一锁对象,减少锁的嵌套
*
*
* new Thread(new Runnable(){ new Thread(new Runnable(){
* @Override @Override
* public void run(){ public void run(){
* synchronizd(p){ 先用打印机 synchronizd(s){ 先用扫描仪
* p.print(); 互不相让,死锁 s.scan();
* synchronizd(s){ 再用扫描仪 synchronizd(p){ 再用打印机
* s.scan(); p.print();
* } }
* } }
* } }
*
* }).start(); }).start();
*
*
* 2.6 活锁(互相让路)
* 这个资源没有被任何线程持有占用,导致程序无法往下执行
* */
public class ThreadDemo02ConcurrentAndLock {
public static void main(String[] args) throws IOException{
//把票数写到properties文件,重新写需求,不用在加载
//反序列化
//1.创建Properties对象
Properties prop=new Properties();
//2..反序列化对象 .load(字节输入流)
prop.load(new FileInputStream("ticket.properties"));
//3.获取元素 .getProperty(key)
int count=Integer.parseInt(prop.getProperty("count"));
Ticket t=new Ticket();
t.setCount(count);
Seller s1=new Seller(t);
Seller s2=new Seller(t);
Seller s3=new Seller(t);
Seller s4=new Seller(t);
//分配新的 Thread 对象。Thread(Runnable target) 把Runable放入Thread()实现,启动Thread.start();
//注意2.获取当前正在执行的线程Thread.currentThread(),然后获取这个线程的名字getName()
// 设置这个线程的名字在 new Thread(Runnable,name).start()
new Thread(s1,"员工1").start();
new Thread(s2,"员工2").start();
new Thread(s3,"员工3").start();
new Thread(s4,"员工4").start();
}
}
//售票员类
class Seller implements Runnable{
//private static int count=100; 共享门票 static,改一次运行一次,想减少启动次数
private Ticket t;
public Seller(Ticket t) {
this.t=t;
}
@Override
public void run() {
// TODO Auto-generated method stub
//while(true)线程不断循环运行
while(true) {
//注意3:同步代码块synchronized---需要一把锁
// 锁要求所有的线程都的认识 while(true) {synchronized(t) {把判断结果放里面,不满足break}}
//也可以synchronized(Seller.class)
//Seller.class表示Seller对应的类
//Seller类的字节码是存在方法区,方法区的类的是被所有线程共享的
//所以也可以synchronized(Math.class)
synchronized(t) {
if(t.getCount()<=0) //把判断结果放里面,不满足break
break; //把判断放在同步代码块外边,出现负数 (一个线程先进来只判断大于0后续无操作,另一个线程直接set-1, 第一个线程接着set-1)
try {
//注意1.让当前线程陷入睡眠状态 Thread.sleep(ms)
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.setCount(t.getCount()-1);
//注意2.获取当前正在执行的线程Thread.currentThread(),然后获取这个线程的名字getName()
// 设置这个线程的名字在 new Thread(Runnable,name).start();
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+t.getCount()+"张票");
/*
* while(t.getCount()-1){ //1负数
* //2.2 //2.1相同票数
* t.setCount(t.getCount()-1); //2 跳过一些数 注意:线程从哪被抢走,从哪接着继续往下走,不是重头再来
* System.out.pringtln(t.getCount());//3
* }
*
*/
//----------------------注意:线程从哪被抢走,从哪接着继续往下走,不是重头再来-----------------------------------------------
//问1:出现一样的 员工2卖了一张票,还剩1张 员工3卖了一张票,还剩1张票
// 思想:一个线程先进来计算完set里面的东西未set,但是另一个线程后进来直接走完set,get
// 第一个线程只算到2.1
//原因:一个线程计算完未set但是set里的get已经读取为100,还是100,而另一个线程抢过来直接set读取为99,最后第一个线程set,读取也是99
//问2:出现-1 员工3卖了一张票,还剩-1张票
//思想: 一个线程先进来只判断大于0后续无操作,另一个线程直接set-1, 第一个线程接着set-1
// 第一个线程只算到1
//原因:总票数1 ,一个线程现进来只判断了大于0,这是另一个进程先set-1为0,第一个线程接着set-1为-1,所以出现-1
//问3:有些数被跳过
//思想: 第一个线程只算完1,2 还未开始3
//原因: 一个线程先进来set-1未打印,另一个线程进来set-1再get打印-2,第一个线程get也是-2跳过-1
}
}
}
}
//票类
class Ticket{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
package ThreadDemo;
/*
* 等待唤醒机制:
* wait,notify来调用线程的执行顺序
* 注意:等待唤醒机制必须结合锁来使用,而且锁对象是谁就用谁进行等待唤醒。
*
* wait(): 表示让当前线程陷入等待
notify():表示唤醒其他任意一个在等待的线程
* 加一个flag标志位, 防止在交换的时候又被抢到资源而不交换
*/
public class ThreadDemo03WaitAndNotifyAndFlag {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s=new Student();
s.setName("柱子");
new Thread(new Ask(s)).start(); //问问题
new Thread(new Change(s)).start(); //交替问问题
}
}
//问问题
class Ask implements Runnable{
private Student s;
public Ask(Student s) {
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
//while(true)线程不断循环运行
while(true) {
synchronized(s) {
if(s.useflag==false) {
try {
//wait:表示让当前线程陷入等待
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("老师,我是"+s.getName()+",我想问个问题");
s.useflag=false; //防止在交换的时候抢到资源,谁抢到谁会执行,如果是flase,即使抢到资源也要ASK线程等待
//notify:表示唤醒其他任意一个在等待的线程
s.notify();
}
}
}
}
//交替问问题
class Change implements Runnable{
private Student s;
public Change(Student s) {
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
//while(true)线程不断循环运行
while(true) {
synchronized(s) {
if(s.useflag==true) {
try {
//wait:表示让当前线程陷入等待
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(s.getName().equals("柱子")) {
s.setName("翠花");
}else {
s.setName("柱子");
}
s.useflag=true; //防止在交换的时候抢到资源,谁抢到谁会执行,如果是true即使抢到资源也要Change线程等待
//notify:表示唤醒其他任意一个在等待的线程
s.notify();
}
}
}
}
class Student {
//做一个标记 防止在交换的时候又被抢到资源而不交换
//如果flag的值为true, 让Ask线程执行,让Change线程等待
//如果flag的值为false,让Change线程执行,让Ask线程等待
//单独定义了一个变量来进行状态的标记--------标记位
public static boolean useflag=true;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package ThreadDemo;
import java.util.Scanner;
/*
* 生产消费模型
*
* 一个线程表示生产者, 一个线程表示消费者,中间通道是商品,总数量不能超过1000
*
* 生产者生产了300个商品,消费者此次最多消费300个商品
* 消费者消费了130个商品,剩余170个商品
* 生产者还可以最多生产830个,此次生产550个,消费者此次最多消费720个商品
* 消费者消费了50个商品,剩余670个商品
* 生产者还可以最多生产330个,此次生产330个,消费者此次最多消费1000个商品
* 消费者消费了0个商品,剩余1000个商品
* 生产者生产了0个商品,还可生产0个
*
*/
public class ThreadExer {
public static void main(String[] args) {
Produce goods=new Produce();
Producers p=new Producers(goods);
Consumers c=new Consumers(goods);
new Thread(p).start();
new Thread(c).start();
}
}
class Producers implements Runnable{
private Produce p;
public Producers(Produce p) {
this.p=p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized(p) {
if(p.flag==false) {
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//计算本次所能生产的最大数量
int max=1001-p.getCount();
//计算本次生产的商品数量
int num = (int) (Math.random() * max);
//计算本次提供的商品数量
p.setCount(p.getCount()+num);
System.out.println("本次生产了" + num + "件商品,能提供" +p.getCount()+ "件商品");
p.flag=false;//防止在交换的时候抢到资源,谁抢到谁会执行,如果是flase,即使抢到资源也要生产线程等待
p.notify();
}
}
}
}
class Consumers implements Runnable{
private Produce p;
public Consumers(Produce p) {
this.p=p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized(p) {
if(p.flag==true) {
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//计算本次可消费的最大数量
int max=p.getCount();
//计算本次消费的数量
int num=(int)(Math.random()*(max+1));
//计算目前剩余数量
p.setCount(p.getCount()-num);
System.out.println("本次消费了"+num+"个,剩余了"+p.getCount()+"个");
p.flag=true; //防止在交换的时候抢到资源,谁抢到谁会执行,如果是true,即使抢到资源也要消费线程等待
p.notify();
}
}
}
}
class Produce{
//如果flag=true生产商品
//如果flag=false消费商品
public boolean flag=true;//标志位,防止在交换的时候抢到资源,谁抢到谁会执行
private int count; //数目
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
package ThreadDemo;
import java.util.Scanner;
/*
* 生产消费模型
*
* 这里改为两个生产者两个消费者
* 改进
* 1.if改while防止连续出现两次生产两次消费
* 2.notify改notifyAll(),去唤醒非同类线程,防止全部陷入wait状态
*
*
*
*
*/
public class ThreadExer02 {
public static void main(String[] args) {
Produce02 goods=new Produce02();
Producers02 p1=new Producers02(goods); //这里改为两个生产者两个消费者
Producers02 p2=new Producers02(goods);
Consumers02 c1=new Consumers02(goods);
Consumers02 c2=new Consumers02(goods);
new Thread(p1).start();
new Thread(p2).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
class Producers02 implements Runnable{
private Produce02 p;
public Producers02(Produce02 p) {
this.p=p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized(p) {
//改进1:之前是if(p.flag==flase)改为while(p.flag==flase)
while(p.flag==false) { //1.出现连着两次生产两次消费
// if改while,是因为假如两个生产者一号生产者在if里面的p.wait()等待,
//下次二号生产者唤醒它的时候继续往下执行代码,而不是if继续判断,因为if只执行一次,会出现连着两次生产两次消费
//所以if改为while,每次判断
//2.改为while会卡住
// wait()里面有 p1,c1,c2 外面有p2
// p2执行完 flag=flase,唤醒p1 同类线程
// wait()里面有 p2,c1,c2,p1判断flag是flase,这样wait()里面有p1,p2,c1,c2
//所以notify()改为notifyAll()唤醒所有线程,肯定有非同类线程
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//计算本次所能生产的最大数量
int max=1000-p.getCount();
//计算本次生产的商品数量
System.out.println("生产者:请输入你要生产的商品数目,目前最大生产"+max+"个");
Scanner scan=new Scanner(System.in);
int num=scan.nextInt();
//计算本次提供的商品数量
p.setCount(p.getCount()+num);
p.flag=false;
//唤醒所有线程
p.notifyAll(); //改进2.改为notifyAll()之前是notify(),保证唤醒的肯定有非同类线程
}
}
}
}
class Consumers02 implements Runnable{
private Produce02 p;
public Consumers02(Produce02 p) {
this.p=p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized(p) {
//之前是if(p.flag==true)改为while(p.flag==true)
while(p.flag==true) { //1.出现连着两次生产两次消费
// if改while,是因为假如两个生产者一号生产者在if里面的p.wait()等待,
//下次二号生产者唤醒它的时候继续往下执行代码,而不是if继续判断,因为if只执行一次,会出现连着两次生产两次消费
//所以if改为while,每次判断
//2.改为while会卡住
// wait()里面有 p1,c1,c2 外面有p2
// p2执行完 flag=flase,唤醒p1 同类线程
// wait()里面有 p2,c1,c2,p1判断flag是flase,这样wait()里面有p1,p2,c1,c2
//所以notify()改为notifyAll()唤醒所有线程,肯定有非同类线程
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//计算本次可消费的最大数量
int max=p.getCount();
//计算本次消费的数量
System.out.println("消费者:请输入你要消费的数目,目前可最大消费"+max+"个");
Scanner scan=new Scanner(System.in);
int num=scan.nextInt();
//计算目前剩余数量
p.setCount(p.getCount()-num);
p.flag=true; //表示准备生产
//唤醒所有线程
p.notifyAll(); //2.改为notifyAll()之前是notify(),保证唤醒的肯定有非同类线程
}
}
}
}
class Produce02{
//如果flag=true生产商品,如果flag=false消费商品
public boolean flag=true;//标志位
private int count; //数目
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
package ThreadDemo;
/*
*
* 1.线程状态
* 阻塞
* ↑ / ↑ \
* / / | \
* / ↓ | ↓
* 创建 --->就绪--->运行--->消亡
*
*
* 2.线程的优先级Priority
* .getPriority()获取优先级
* .setPriority()设置优先级
*
* 线程的优先级1-10 ,默认优先级是5
* 理论上,数字越大优先级越高,抢占到资源的概率就越大
* 实际上,相邻的两个优先级非常不明显,如果优先级差到5个单位以上,则结果会相对明显一点点
*
*
*
*
*
*
*
*/
public class ThreadDemo04Priority {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread(new Priority(),"我是线程A");
Thread t2=new Thread(new Priority(),"我是线程B");
//获取线程的优先级(未设置) .getPriority()
System.out.println("线程的默认优先级是"+t1.getPriority()); //输出5,默认优先级5
System.out.println("线程的默认优先级是"+t2.getPriority()); //输出5,默认优先级5
//设置线程的优先级 .setPriority()
t1.setPriority(5);
t2.setPriority(6);
t1.start(); //次数差不多,相邻的两个优先级非常不明显
t2.start(); //如果优先级差到5个单位以上,则结果会相对明显一点点
}
}
class Priority implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=1;i<10;i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
package ThreadDemo;
/*
* 3.守护线程
* 守护别的线程,只要被守护的线程结束,那么无论守护线程完成与否都会结束。(守护线程可以先死)
* 在线程中,一个线程要么是守护线程,要么是被守护的线程,只有当最后一个被守护的线程结束才会导致所有的守护线程结束---GC最大的守护线程
*
* 10个线程 7个守护 另外3个默认都是被守护线程 当3个最后一个被守护的线程结束,所有的守护线程结束。
*
* 设置守护线程 守护线程可以先死 .setDemo(boolean)
*
* 4.启动线程的情况
* 4.1用户请求创建
* 4.2系统自启
* 4.3被其他线程唤醒启动
*
* 5.线程结束的情况
* 5.1线程执行完成之后自然关闭 -寿终正寝
* 5.2线程被其他请求强制结束(例如守护线程,手动关闭代码执行) -他杀
* 5.3线程执行过程中出现了异常或者是错误 -意外事故
*/
public class ThreadDemo05Daemon {
public static void main(String[] args) {
Thread t1=new Thread(new soldier(),"小兵1");
Thread t2=new Thread(new soldier(),"小兵2");
Thread t3=new Thread(new soldier(),"小兵3");
Thread t4=new Thread(new soldier(),"小兵4");
//设置守护线程 守护线程可以先死 .setDemo(boolean)
t1.setDaemon(true);
t2.setDaemon(true);
t3.setDaemon(true);
t4.setDaemon(true);
t1.start();
t2.start();
t3.start();
t4.start();
//主线程为boss
for(int i=10;i>0;i--) {
System.out.println("BOOS掉了一滴血,剩余血量"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class soldier implements Runnable{
//守护线程:小兵
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=1000;i>0;i--) {
System.out.println(Thread.currentThread().getName()+"掉了一滴血,剩余血量"+i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package ThreadDemo;
/*
* 6.单例模式--在全局中只存在一个实例的这种现象
*
* 设计模式:在软件开发工程中使用的常见的解决问题的方式
*
* A饿汉式:无论需不需要对象都会初始化对象增加类的执行时间 调用static方法,不用实例化对象,能避免并发问题
* B懒汉式:减少加载时候,在用到对象的时候在去实例化对象,会导致多线程的并发安全问题(线程抢占,创建对象)
*
*
*-----------------------单例模式的七种定义方式-----------------------------------
*/
public class ThreadDemo06SingletonPattern {
public static void main(String[] args) {
TaskManager tm=TaskManager.getInstance(); //初始化,获取对象
TaskManager tm2=TaskManager.getInstance();
TaskManager.kill();
}
}
//A饿汉式,无论需不需要对象都会初始化对象会增加类的执行时间 调用static方法,不用实例化对象,能避免并发问题
class TaskManager{
//B在定义的时候已经进行初始化--饿汉式
//2.提供本类静态对象,保证对象唯一性
private static TaskManager tm=new TaskManager();
//1.构造方法私有化,不允许在类外随意创建对象
private TaskManager() {
}
//3.提供一个静态方法,来允许对外获取对象,在方法中对对象进行初始化操作
public static TaskManager getInstance() { //static如果不存在,外面无法进行创建对象,不创建对象就不能调用方法,所以改为static
//对tm对象进行初始化操作
return tm;
}
public static void kill() {
}
}
//B懒汉式,减少加载时候,在用到对象的时候在去实例化对象,会导致多线程的并发安全问题(线程抢占,创建对象)
class TaskManager02{
//2.提供本类静态对象,保证对象唯一性
private static TaskManager02 tm;
//1.构造方法私有化,不允许在类外随意创建对象
private TaskManager02() {
}
//B把创建的时候给推后了---------懒汉式
//保证在用到这个对象的时候在创建
public static TaskManager02 getInstance() { //static如果不存在,外面无法进行创建对象,不创建对象就不能调用方法,所以改为static
if(tm==null)tm=new TaskManager02(); //多线程的并发安全问题
//对tm对象进行初始化操作 //第一个线程判断null,未创建对象,第二个线程判断null马上创建对象,第一个线程接着也创建对象
return tm;
}
}