绍兴网站的优化咸阳网站建设费用

bicheng/2026/1/16 13:42:06/文章来源:
绍兴网站的优化,咸阳网站建设费用,高级网站开发工信部,wordpress当DAM用文章目录 6.线程安全6.1 线程安全问题6.2 线程同步机制6.3 关于线程同步的面试题6.3.1 版本16.3.2 版本26.3.3 版本36.3.4 版本4 7.死锁7.1 多线程卖票问题 8.线程通信8.1 wait()和sleep的区别#xff1f;8.2 两个线程交替输出8.3 三个线程交替输出8.4 线程通信-生产者和消费者… 文章目录 6.线程安全6.1 线程安全问题6.2 线程同步机制6.3 关于线程同步的面试题6.3.1 版本16.3.2 版本26.3.3 版本36.3.4 版本4 7.死锁7.1 多线程卖票问题 8.线程通信8.1 wait()和sleep的区别8.2 两个线程交替输出8.3 三个线程交替输出8.4 线程通信-生产者和消费者模式 9.线程生命周期的回顾与补充10.单例模式10.1 饿汉式单例模式10.2 懒汉式单例模式10.3 懒汉式单例模式可能会造成线程安全问题 11.可重入锁ReentrantLock12.实现线程的第三种方式实现Callable接口13.实现线程的第四种方式使用线程池 接上一篇 JavaSE-10笔记【多线程1】 6.线程安全 6.1 线程安全问题 什么情况下需要考虑线程安全问题 多线程并发的环境下有共享的数据且涉及到共享数据的修改操作。 一般情况下 ①局部变量若为基本数据类型则不存在线程安全问题【在栈中栈不是共享的】而若为引用数据类型则另说了。 ②实例变量可能存在线程安全问题实例变量在堆中堆是多线程共享的。 ③静态变量也可能存在安全问题静态变量在堆中堆是多线程共享的。 多个线程并发对同一个银行账户进行取款操作时会有安全问题t1线程在取了一笔钱后由于网络卡顿没有及时更新余额此时t2线程又将未更新的数据读取到取了一笔钱导致两个线程都取出了一笔钱而余额只少了一笔。 如何解决 将t1线程和t2线程排队执行不要并发要排队。这种排队机制被称作“线程同步机制”。【对于t1线程和t2线程t2线程在执行的时候必须等待t1线程执行到某个位置之后t2线程才能执行。t1和t2之间发生了等待则认为是同步。 如果不排队则被称为“线程异步机制”t1和t2线程各自执行各自的并发执行互相不需要等待。 异步效率高但不安全。 同步安全但效率低。 示例代码 package threadtest.thread14;public class ThreadTest14 {public static void main(String[] args) {//创建账户对象Account account new Account(54234657, 10000);//t1线程和t2线程共享一个账户Thread t1 new Thread(new MyRunnable(account));t1.setName(t1);Thread t2 new Thread(new MyRunnable(account));t2.setName(t2);//启动线程t1.start();t2.start();} }class MyRunnable implements Runnable{private Account account;public MyRunnable(Account account) {this.account account;}Overridepublic void run() {account.withDraw(1000);} } class Account{private int actNo; //账户编号private double balance; //账户余额public Account(int actNo, double balance) {this.actNo actNo;this.balance balance;}public int getActNo() {return actNo;}public void setActNo(int actNo) {this.actNo actNo;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance balance;}/*** 取款* param money 取款额度*/public void withDraw(double money){//为了演示出多线程并发带来的安全问题这里将取款分为两步//1. 获取余额double before this.getBalance();System.out.println(Thread.currentThread().getName() 线程正在取款 money 当前账户 this.getActNo() 的账户余额为 before);try {Thread.sleep(1000); //为了演示线程安全问题这里让当前线程睡眠1秒} catch (InterruptedException e) {throw new RuntimeException(e);}//2. 取款后修改余额this.setBalance(before - money);System.out.println(Thread.currentThread().getName() 线程取款成功当前账户 this.getActNo() 的账户余额为 this.getBalance());} }运行结果取款2次余额有误 6.2 线程同步机制 使用线程同步机制本质是线程排队执行来避免多线程并发的线程安全问题 语法格式 synchronized(必须是需要排队的这几个线程共享的对象){//需要同步的代码 }必须是需要排队的这几个线程共享的对象必须选对了如果选错了可能会无故增加同步线程的数量导致效率降低。 原理 synchronized(obj){//需要同步的代码 }假设obj是t1和t2两个线程共享的t1和t2执行这段代码的时候一定有先后顺序的一定是有一个先抢到了CPU时间片。 假设t1先抢到了CPU时间片t1线程找共享对象obj的对象锁找到之后占有这把锁只要能够占有obj对象的对象锁就有权利进入同步代码块执行代码。当t1线程执行完同步代码块之后会释放之前占有的对象锁归还锁。同样t2线程抢到CPU时间片之后执行到这段代码也会去找对象obj的对象锁但由于t1线程占有这把锁t2线程只能在同步代码块之外等待直到t1归还锁才能执行同步代码块的代码。 注意不要无故扩大同步代码块的范围其范围越小效率越高。 示例代码 package threadtest.thread14;public class ThreadTest14 {public static void main(String[] args) {//创建账户对象Account account new Account(54234657, 10000);//t1线程和t2线程共享一个账户Thread t1 new Thread(new MyRunnable(account));t1.setName(t1);Thread t2 new Thread(new MyRunnable(account));t2.setName(t2);//启动线程t1.start();t2.start();} }class MyRunnable implements Runnable{private Account account;public MyRunnable(Account account) {this.account account;}Overridepublic void run() {account.withDraw(1000);} } class Account{private int actNo; //账户编号private double balance; //账户余额public Account(int actNo, double balance) {this.actNo actNo;this.balance balance;}public int getActNo() {return actNo;}public void setActNo(int actNo) {this.actNo actNo;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance balance;}/*** 取款* param money 取款额度*/public void withDraw(double money) {//为了演示出多线程并发带来的安全问题这里将取款分为两步synchronized (this) { //由于这里两个线程共享的是同一个account对象所以这里可以直接填this其他情况要再另外分析并不一定都是填this//1. 获取余额double before this.getBalance();System.out.println(Thread.currentThread().getName() 线程正在取款 money 当前账户 this.getActNo() 的账户余额为 before);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//2. 取款后修改余额this.setBalance(before - money);System.out.println(Thread.currentThread().getName() 线程取款成功当前账户 this.getActNo() 的账户余额为 this.getBalance());}} }也可以在实例方法上添加synchronized关键字 在实例方法上添加了synchronized关键字之后整个方法体就是一个同步代码块。在实例方法上添加了synchronized关键字之后共享对象的对象锁一定是当前对象this的对象锁。 这种方式相对于上面的局部同步代码块的方式要差一些利用局部同步代码块的优点 共享对象可以随便调整同步代码块的范围可以随便调整。 示例代码 package threadtest.thread14;public class ThreadTest14 {public static void main(String[] args) {//创建账户对象Account account new Account(54234657, 10000);//t1线程和t2线程共享一个账户Thread t1 new Thread(new MyRunnable(account));t1.setName(t1);Thread t2 new Thread(new MyRunnable(account));t2.setName(t2);//启动线程t1.start();t2.start();} }class MyRunnable implements Runnable{private Account account;public MyRunnable(Account account) {this.account account;}Overridepublic void run() {account.withDraw(1000);} } class Account{private int actNo; //账户编号private double balance; //账户余额public Account(int actNo, double balance) {this.actNo actNo;this.balance balance;}public int getActNo() {return actNo;}public void setActNo(int actNo) {this.actNo actNo;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance balance;}/*** 取款* param money 取款额度*/public synchronized void withDraw(double money) { //直接在方法上加上synchronized关键字//为了演示出多线程并发带来的安全问题这里将取款分为两步//1. 获取余额double before this.getBalance();System.out.println(Thread.currentThread().getName() 线程正在取款 money 当前账户 this.getActNo() 的账户余额为 before);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//2. 取款后修改余额this.setBalance(before - money);System.out.println(Thread.currentThread().getName() 线程取款成功当前账户 this.getActNo() 的账户余额为 this.getBalance());} }运行结果同上。 6.3 关于线程同步的面试题 分析以下程序m2方法在执行的时候需要等待m1方法的结束吗 6.3.1 版本1 package threadtest.thread15;public class ThreadTest15 {public static void main(String[] args) {MyClass mc new MyClass();Thread t1 new Thread(new MyRunnable(mc));Thread t2 new Thread(new MyRunnable(mc));t1.setName(t1);t2.setName(t2);t1.start();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}t2.start();} } class MyRunnable implements Runnable{private MyClass mc;public MyRunnable(MyClass mc){this.mc mc;}Overridepublic void run() {if(t1.equals(Thread.currentThread().getName())){mc.m1();}if(t2.equals(Thread.currentThread().getName())){mc.m2();}} } class MyClass{public synchronized void m1(){ //同步方法System.out.println(m1 begin);try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(m1 over);}public void m2(){ //非同步方法System.out.println(m2 begin);System.out.println(m2 over);} }运行结果 不需要等待。 线程t1执行的是同步方法m1()需要获取对象锁 线程t2执行的是非同步方法m2()并不需要获取对象锁。所以线程t2不需要等待线程t1。 6.3.2 版本2 package threadtest.thread15;public class ThreadTest15 {public static void main(String[] args) {MyClass mc new MyClass();Thread t1 new Thread(new MyRunnable(mc));Thread t2 new Thread(new MyRunnable(mc));t1.setName(t1);t2.setName(t2);t1.start();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}t2.start();} } class MyRunnable implements Runnable{private MyClass mc;public MyRunnable(MyClass mc){this.mc mc;}Overridepublic void run() {if(t1.equals(Thread.currentThread().getName())){mc.m1();}if(t2.equals(Thread.currentThread().getName())){mc.m2();}} } class MyClass{public synchronized void m1(){ //同步方法System.out.println(m1 begin);try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(m1 over);}public synchronized void m2(){ //同步方法System.out.println(m2 begin);System.out.println(m2 over);} }运行结果 需要等待。 由于线程t1执行的是同步方法m1()需要获取对象锁 线程t2执行的也是同步方法m2()也需要获取对象锁。而线程t1和线程t2共享同一个对象所以线程t2需要等待线程t1。 6.3.3 版本3 package threadtest.thread15;public class ThreadTest15 {public static void main(String[] args) {MyClass mc1 new MyClass();MyClass mc2 new MyClass();Thread t1 new Thread(new MyRunnable(mc1));Thread t2 new Thread(new MyRunnable(mc2));t1.setName(t1);t2.setName(t2);t1.start();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}t2.start();} } class MyRunnable implements Runnable{private MyClass mc;public MyRunnable(MyClass mc){this.mc mc;}Overridepublic void run() {if(t1.equals(Thread.currentThread().getName())){mc.m1();}if(t2.equals(Thread.currentThread().getName())){mc.m2();}} } class MyClass{public synchronized void m1(){ //同步方法System.out.println(m1 begin);try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(m1 over);}public synchronized void m2(){ //同步方法System.out.println(m2 begin);System.out.println(m2 over);} }运行结果 不需要等待。 虽然m1和m2方法都是同步方法但是线程t1和线程t2并没有共享对象其分别调用不同对象各自占用各自对象的对象锁即可不存在需要同一对象锁的问题。 6.3.4 版本4 package threadtest.thread15;public class ThreadTest15 {public static void main(String[] args) {MyClass mc1 new MyClass();MyClass mc2 new MyClass();Thread t1 new Thread(new MyRunnable(mc1));Thread t2 new Thread(new MyRunnable(mc2));t1.setName(t1);t2.setName(t2);t1.start();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}t2.start();} } class MyRunnable implements Runnable{private MyClass mc;public MyRunnable(MyClass mc){this.mc mc;}Overridepublic void run() {if(t1.equals(Thread.currentThread().getName())){mc.m1();}if(t2.equals(Thread.currentThread().getName())){mc.m2();}} } class MyClass{public static synchronized void m1(){ //静态同步方法System.out.println(m1 begin);try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(m1 over);}public static synchronized void m2(){ //静态同步方法System.out.println(m2 begin);System.out.println(m2 over);} }运行结果 需要等待。 因为此时m1和m2方法都是静态方法且又都加上了synchronized关键字此时线程同步时会找类锁。类锁是对于一个类来说只有一把锁不管创建了多少个对象类锁都只有一把。 总结 在静态方法上添加synchronized关键字实际上是为了保证静态变量的安全 在实例方法上添加synchronized关键字实际上是为了保证实例变量的安全。 7.死锁 当有多个线程同时共享多个对象时可能会发生死锁问题 示例代码 package threadtest.thread16;public class ThreadTest16 {public static void main(String[] args) {Object o1 new Object();Object o2 new Object();Thread t1 new Thread(new MyRunnable(o1,o2));Thread t2 new Thread(new MyRunnable(o1,o2));t1.setName(t1);t2.setName(t2);t1.start();t2.start();} }class MyRunnable implements Runnable{private Object o1;private Object o2;public MyRunnable(Object o1, Object o2) {this.o1 o1;this.o2 o2;}Overridepublic void run() {if(Thread.currentThread().getName().equals(t1)){synchronized (o1){try {Thread.sleep(1000); //为了产生死锁这里设置睡眠} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){}}}if(Thread.currentThread().getName().equals(t2)){synchronized (o2){try {Thread.sleep(1000); //为了产生死锁这里设置睡眠} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){}}}} }以上代码线程t1先占用o1对象的对象锁线程t2先占用o2对象的对象锁而后线程t1又需要寻找o2对象的对象锁线程t2又需要寻找o1对象的对象锁由于所需的对象锁分别被对方占用所以只能陷入无休止的互相等待中。造成死锁。 运行结果永远无法结束 死锁容易发生在synchronized嵌套中所以对synchronized要慎重使用。 7.1 多线程卖票问题 3个线程同时卖票不排队 package threadtest.thread17;public class ThreadTest17 {public static void main(String[] args) {//创建一个对象让多个线程共享一个对象Runnable r new MyRunnable();//创建3个线程模拟3个售票窗口Thread t1 new Thread(r);t1.setName(1);Thread t2 new Thread(r);t2.setName(2);Thread t3 new Thread(r);t3.setName(3);t1.start();t2.start();t3.start();} }class MyRunnable implements Runnable{private int ticketNum 100;Overridepublic void run() {while (true) {if(ticketNum 0){System.out.println(票已售完);break; //停止售票}//票还有try {//出票等待时间Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(窗口 Thread.currentThread().getName() 成功售出一张票当前剩余票数 (--ticketNum));}} }运行结果出现数据问题剩余票数出现负数 排队卖票即使用线程同步机制 package threadtest.thread17;public class ThreadTest17 {public static void main(String[] args) {//创建一个对象让多个线程共享一个对象Runnable r new MyRunnable();//创建3个线程模拟3个售票窗口Thread t1 new Thread(r);t1.setName(1);Thread t2 new Thread(r);t2.setName(2);Thread t3 new Thread(r);t3.setName(3);t1.start();t2.start();t3.start();} }class MyRunnable implements Runnable{private int ticketNum 100;Overridepublic void run() {synchronized (this) { //共享对象就是当前对象while (true) {if(ticketNum 0){System.out.println(票已售完);break; //停止售票}//票还有try {//出票等待时间Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(窗口 Thread.currentThread().getName() 成功售出一张票当前剩余票数 (--ticketNum));}}} }运行结果 8.线程通信 线程通信的三个方法wait()、notify()、notifyAll() wait(): 线程执行该方法后进入等待状态并且释放对象锁。notify(): 唤醒优先级最高的那个等待状态的线程。【优先级相同的随机选一个】。被唤醒的线程从当初wait()的位置继续执行。notifyAll(): 唤醒所有wait()的线程。 例如对于多线程共享的对象obj调用了obj.wait()之后在obj对象上活跃的所有线程都进入无期限等待直到调用了该共享对象的notify()方法进行了唤醒。唤醒后会接着上一次调用wait()方法的位置继续向下执行。 需要注意的 这三个方法都是Object类的方法。以上三个方法在使用时必须在同步代码块中或同步方法中。调用这三个方法的对象必须是共享的锁对象。 8.1 wait()和sleep的区别 相同点 都会阻塞。 不同点 wait是Object类的实例方法。sleep是Thread的静态方法。wait只能用在同步代码块或同步方法中。sleep随意。wait方法执行会释放对象锁。sleep不会。wait结束时机是notify唤醒或达到指定时间。sleep结束时机是到达指定时间。 wait()方法有三个重载方法 wait()调用此方法线程进入“等待状态”wait(毫秒)调用此方法线程进入“超时等待状态”wait(毫秒, 纳秒)调用此方法线程进入“超时等待状态”。 8.2 两个线程交替输出 要求创建两个线程交替输出1-100如下 t1–1 t2–2 t1–3 t2–4 t1–5 t2–6 … 直到100 需要交替输出则需要进行线程间的通信代码如下 package threadtest.thread18;public class ThreadTest18 {public static void main(String[] args) {Runnable r new MyRunnable();Thread t1 new Thread(r);t1.setName(t1);Thread t2 new Thread(r);t2.setName(t2);t1.start();t2.start();} }class MyRunnable implements Runnable{private int count 0;Overridepublic void run() {synchronized (this) {while (true) {//前面t1释放锁后t2线程开始占用对象锁开始执行这里同步代码块的内容//这里需要记得唤醒t1线程//t2线程执行过程中把t1唤醒了但是由于t2仍然占用对象锁所以即使t1醒了也不会往下执行需要等到t2释放对象锁this.notify();if (count 100) {break;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() -- (count));try {//让其中一个线程等待这个等待的线程可能是t1也可能是t2//假设目前执行的是t1线程则t1线程释放对象锁进入无限期的等待直到notify()唤醒this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}} }8.3 三个线程交替输出 要求创建三个线程交替输出A、B、C如下 t1–A t2–B t3–C t1–A t2–B t3–C … 按照以上输出10遍以上是2遍的示例 代码如下 package threadtest.thread19;public class ThreadTest19 {// 三个静态输出标记值初始值表示第一次输出的时候t1先输出static boolean t1Output true;static boolean t2Output false;static boolean t3Output false;public static void main(String[] args) {//共享对象t1、t2、t3线程共享一个对象Object lock new Object();//t1线程负责输出AThread t1 new Thread(new Runnable() {Overridepublic void run() {//同步代码块synchronized (lock){for (int i 0; i 10; i) {while (!t1Output){ //只要不是t1输出t1Output为false则让线程进入等待状态try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}//轮到t1输出了并且t1线程被唤醒了System.out.println(Thread.currentThread().getName()--A);//修改布尔标记的值t1Output false;t2Output true;t3Output false;//唤醒所有线程lock.notifyAll();}}}});t1.setName(t1);t1.start();//t2线程负责输出BThread t2 new Thread(new Runnable() {Overridepublic void run() {synchronized (lock){for (int i 0; i 10; i) {while (!t2Output){try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}//轮到t2输出了并且t2线程被唤醒了System.out.println(Thread.currentThread().getName()--B);//修改布尔标记的值t1Output false;t2Output false;t3Output true;//唤醒所有线程lock.notifyAll();}}}});t2.setName(t2);t2.start();//t3线程负责输出CThread t3 new Thread(new Runnable() {Overridepublic void run() {synchronized (lock){for (int i 0; i 10; i) {while (!t3Output){try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}//轮到t3输出了并且t3线程被唤醒了System.out.println(Thread.currentThread().getName()--C);//修改布尔标记的值t1Output true;t2Output false;t3Output false;//唤醒所有线程lock.notifyAll();}}}});t3.setName(t3);t3.start();} }8.4 线程通信-生产者和消费者模式 线程通信可以实现生产者和消费者均衡 9.线程生命周期的回顾与补充 完善线程的生命周期图 10.单例模式 10.1 饿汉式单例模式 package singleton;/*** 饿汉式单例模式*/ public class Singleton {private static Singleton singleton new Singleton();private Singleton(){}public static Singleton getSingleton(){return singleton;}public static void main(String[] args) {Singleton singleton1 Singleton.getSingleton();Singleton singleton2 Singleton.getSingleton();System.out.println(singleton1 singleton2); //true} }10.2 懒汉式单例模式 package singleton;/*** 懒汉式单例模式*/ public class Singleton {private static Singleton singleton;private Singleton(){}public static Singleton getSingleton(){if(singleton null){singleton new Singleton();}return singleton;}public static void main(String[] args) {Singleton singleton1 Singleton.getSingleton();Singleton singleton2 Singleton.getSingleton();System.out.println(singleton1 singleton2); //true} }10.3 懒汉式单例模式可能会造成线程安全问题 package threadtest.thread20;public class ThreadTest20 {//静态变量private static Singleton s1;private static Singleton s2;public static void main(String[] args) {//创建线程t1Thread t1 new Thread(new Runnable() {Overridepublic void run() {s1 Singleton.getSingleton();}});//创建线程t2Thread t2 new Thread(new Runnable() {Overridepublic void run() {s2 Singleton.getSingleton();}});//启动线程t1.start();t2.start();//保证t1、t2线程在main方法结束前执行(需要线程先start才能起作用)try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}//判断两个Singleton对象是否一样。这里为什么不一样啊System.out.println(s1);System.out.println(s2);System.out.println(s1 s2);} }/*** 懒汉式单例模式*/ class Singleton {private static Singleton singleton;private Singleton(){System.out.println(构造方法执行了);}public static Singleton getSingleton(){if(singleton null){singleton new Singleton();}return singleton;} }运行结果出现构造方法执行2次创建了2个对象 解决方案1同步方法 package threadtest.thread20;public class ThreadTest20 {//静态变量private static Singleton s1;private static Singleton s2;public static void main(String[] args) {//创建线程t1Thread t1 new Thread(new Runnable() {Overridepublic void run() {s1 Singleton.getSingleton();}});//创建线程t2Thread t2 new Thread(new Runnable() {Overridepublic void run() {s2 Singleton.getSingleton();}});//启动线程t1.start();t2.start();//保证t1、t2线程在main方法结束前执行(需要线程先start才能起作用)try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}//判断两个Singleton对象是否一样。这里为什么不一样啊System.out.println(s1);System.out.println(s2);System.out.println(s1 s2);} }/*** 懒汉式单例模式*/ class Singleton {private static Singleton singleton;private Singleton(){System.out.println(构造方法执行了);}//第一种方法同步方法在方法声明处加上synchronized关键字由于又是静态方法让线程排队执行,去找类锁public static synchronized Singleton getSingleton(){if(singleton null){singleton new Singleton();}return singleton;} }运行结果 解决方案2同步代码块 package threadtest.thread20;public class ThreadTest20 {//静态变量private static Singleton s1;private static Singleton s2;public static void main(String[] args) {//创建线程t1Thread t1 new Thread(new Runnable() {Overridepublic void run() {s1 Singleton.getSingleton();}});//创建线程t2Thread t2 new Thread(new Runnable() {Overridepublic void run() {s2 Singleton.getSingleton();}});//启动线程t1.start();t2.start();//保证t1、t2线程在main方法结束前执行(需要线程先start才能起作用)try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}//判断两个Singleton对象是否一样。这里为什么不一样啊System.out.println(s1);System.out.println(s2);System.out.println(s1 s2);} }/*** 懒汉式单例模式*/ class Singleton {private static Singleton singleton;private Singleton(){System.out.println(构造方法执行了);}//第二种方法同步代码块方法内部设置同步代码块让线程排队执行也去找类锁public static Singleton getSingleton(){synchronized (Singleton.class){ //Singleton.class为反射机制中的内容获取Singleton类if(singleton null){singleton new Singleton();}}return singleton;} }运行结果 解决方案3针对方案2进行优化提高效率 package threadtest.thread20;public class ThreadTest20 {//静态变量private static Singleton s1;private static Singleton s2;public static void main(String[] args) {//创建线程t1Thread t1 new Thread(new Runnable() {Overridepublic void run() {s1 Singleton.getSingleton();}});//创建线程t2Thread t2 new Thread(new Runnable() {Overridepublic void run() {s2 Singleton.getSingleton();}});//启动线程t1.start();t2.start();//保证t1、t2线程在main方法结束前执行(需要线程先start才能起作用)try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}//判断两个Singleton对象是否一样。这里为什么不一样啊System.out.println(s1);System.out.println(s2);System.out.println(s1 s2);} }/*** 懒汉式单例模式*/ class Singleton {private static Singleton singleton;private Singleton(){System.out.println(构造方法执行了);}//第二种方法同步代码块外面再嵌套一个if语句减少一次找类锁的时间public static Singleton getSingleton(){if(singleton null) {synchronized (Singleton.class) { //Singleton.class为反射机制中的内容获取Singleton类if (singleton null) {singleton new Singleton();}}}return singleton;} }解决方案4使用可重入锁ReentrantLock看下面介绍 11.可重入锁ReentrantLock Java还有一个Lock接口从JDK5开始引入Lock接口下有一个实现类可重入锁ReentrantLock其也可以实现线程安全且比synchronized更推荐使用因为其更加灵活可以更细粒度地控制同步代码但是一定要记住解锁 注意要想使用ReentrantLock达到线程安全假设要让t1、t2、t3线程同步就需要让t1、t2、t3共享同一个ReentrantLock对象。 语法 保证多个线程共享一个ReentrantLock对象比如如下的private static final ReentrantLock lock new ReentrantLock();然后在需要同步的代码块前面加锁即lock.lock();并在后面解锁即lock.unlock();。 上述懒汉式单例模式保证线程安全 package threadtest.thread21;import java.util.concurrent.locks.ReentrantLock;public class ThreadTest21 {//静态变量private static Singleton s1;private static Singleton s2;public static void main(String[] args) {//创建线程t1Thread t1 new Thread(new Runnable() {Overridepublic void run() {s1 Singleton.getSingleton();}});//创建线程t2Thread t2 new Thread(new Runnable() {Overridepublic void run() {s2 Singleton.getSingleton();}});//启动线程t1.start();t2.start();//保证t1、t2线程在main方法结束前执行(需要线程先start才能起作用)try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}//判断两个Singleton对象是否一样。这里为什么不一样啊System.out.println(s1);System.out.println(s2);System.out.println(s1 s2);} }/*** 懒汉式单例模式*/ class Singleton {private static Singleton singleton;private Singleton(){System.out.println(构造方法执行了);}//创建共享锁对象需要设置为静态的才是共享的private static final ReentrantLock lock new ReentrantLock();//第四种方法使用可重入锁ReentrantLockpublic static Singleton getSingleton(){try {//加锁lock.lock();if (singleton null) {singleton new Singleton();}} finally {//解锁需要100%保证解锁所以需要使用finallylock.unlock();}return singleton;} }运行结果 12.实现线程的第三种方式实现Callable接口 实现线程的第三种方式实现Callable接口。这种方式实现线程可以获取到线程的返回值。 package threadtest.thread22;import java.util.concurrent.Callable; import java.util.concurrent.FutureTask;/*** 实现线程的第三种方式实现Callable接口覆写call()方法*/ public class ThreadTest22 {public static void main(String[] args) {//创建“未来任务”对象设置的泛型为线程的返回类型FutureTaskInteger task new FutureTaskInteger(new CallableInteger() {Overridepublic Integer call() throws Exception { //与run()方法不一样的是call方法可以抛出异常//处理业务Thread.sleep(5000);return 1;}});//创建线程对象Thread t new Thread(task);t.setName(t);//启动线程t.start();try {//获取“未来任务”线程的返回值//会阻塞当前线程等待“未来任务”结束并返回//拿到返回值当前线程的阻塞才会解除继续执行。这里则表现为会等待5秒Integer i task.get();System.out.println(t线程返回值 i);} catch(Exception e){e.printStackTrace();}} }运行结果 13.实现线程的第四种方式使用线程池 线程池本质上就是一个缓存cache。一般都是服务器在启动的时候初始化线程池即服务器在启动的时候创建多个线程对象直接放到线程池中需要使用线程对象的时候直接从线程池中获取。 package threadtest.thread23;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ThreadTest23 {public static void main(String[] args) {//创建一个线程池对象线程池中有3个线程ExecutorService executorService Executors.newFixedThreadPool(3);//将任务交给线程池无需触碰到这个线程对象只需要将要处理的任务交给线程池即可executorService.submit(new Runnable() {Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() -- i);}}});//最后记得关闭线程池executorService.shutdown();} }运行结果这里只用到了线程池中的一个线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/89953.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

想建立一个网站广州黄埔做网站公司

A:饥饿的XP XP迷失在X星球,他醒来时已经很久很久很久没有吃过东西了。他突然发现身边有一张地图,上面有X星球上每一个食物供给点的位置。太好了,XP跳了起来。他决定先把肚子填饱再去寻找其他伙伴。现在已知XP的位置(X, Y),以及他的…

昆山网站优化建设盐城做网站网络公司电话?

Linux 内核简介 现在让我们从一个比较高的高度来审视一下 GNU/Linux 操作系统的体系结构。您可以从两个层次上来考虑操作系统,如图 2 所示。 图 2. GNU/Linux 操作系统的基本体系结构 上面是用户(或应用程序)空间。这是用户应用程序执行的地…

网站续费合同农业公司怎样建立网站

参数相关 date为传入时间 例:"2023/5/10 11:32:01" 2023-02-01 09:32:01type为返回类型 例:- / 年月日 年默认规则,大于等于一天(24小时)展示X天前;大于等于30天且小于365天展示X个月前;大于等于365天且展示…

免费网站建站 知乎音乐网站建设流程

11111 11111111111111111111111111111 555555555

太原优化型网站建设阿里云做淘宝客网站吗

PAC代理和HTTP代理都是网络代理的形式,但它们有一些区别。 PAC代理(Proxy Auto-Config):PAC代理是一种根据特定规则自动选择代理服务器的方式。通过使用一个PAC文件(通常是一个JavaScript文件),…

绵阳做网站哪家公司好DW做网站下拉列表怎么做

一. 下载地址 Go官方下载地址:https://golang.org/dl/ Go中文网:https://go.p2hp.com/go.dev/dl/ 根据不同系统下载不同的包。 二. 配置GOPATH GOPATH是一个环境变量,用来表明你写的go项目的存放路径。 GOPATH路径最好只设置一个&#xff0…

公司网站建设工作内容大连网站建设要多少钱

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、DF概述 4.2、DF基本原理 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2013b 3.部分核心程序 clc; clear; close all; warning off; addpath(genpath(pwd))…

什么网站可以做动画宁波seo网络推广产品服务

目录 警告信息 1、确保安装了Python和pip 2、安装Graphviz软件包 3、pip安装pydot 验证 在gem5中,pydot库用于生成图形化输出,特别是生成.dot文件和相关的图像文件,如PDF、PNG等。它与gem5结合使用的一个常见用途是生成系统结构图、内存…

百度手机网站制作wordpress仿知乎社区

当前,数字时代为中国及其他亚太地区的企业带来了巨大机遇:成功实现数字化转型的企业,能够提升其在全球市场的竞争力。IDC预测,今年亚太地区的企业在数字化转型上的开支将超过3,758亿美元,中国将成为整个亚太地区最大的…

做校招的网站有哪些付费阅读网站代码

在Scala中,迭代器(Iterator)是一种用于遍历集合(如数组、列表、集合等)的元素而不暴露其底层表示的对象。迭代器提供了一种统一的方法来访问集合中的元素,而无需关心集合的具体实现。 在Scala中&#xff0c…

柳城企业网站建设公司配置无法运行wordpress

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 c# wpf如果是用来开发非标上位机的,那么和plc的通信肯定是少不了的。而且,大部分plc都支持modbus协议,所以这个…

模板网站建设教程广东商城网站建设

Google Maps API 教程 在本教程中我们将学习如何使用谷歌地图API V3创建交互式地图。 什么是 API? API = 应用程序编程接口(Application programming interface)。 API(Application Programming Interface,应用编程接口)其实就是操作系统留给应用程序的一个调用接口,…

网站建设和网络维护海洋专业做网站

1 问题 给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值,列如,数组{2,3,4,2,6,2,5,1}的滑动窗口大小是3,一起6个滑动窗口,分别是{4,4,6,6&#…

网站建设流程六个步骤怎么样做企业网站

表格基本上有如下几个标签构成:(1).(2).标签用来创建表格的行。(3).标签用来创建表头单元格。 t-head(4).标签用来创建tr行中的单元格。(5).标签用来创建标题。(6).标签用来创建表格的表头。 (一个table只能有一个)(7).标签用来创建表格的主体部分。(8).标签用来创建表格的页…

网页设计跟网站建设的区别怎样做的网站内网外网都能用

文章目录 背包问题背包题目解法一 ● 01背包问题-二维数组五部曲1.确定dp数组2、确定递推公式3、初始化dp数组4、循环代码: 解法二-01背包问题-滚动数组五部曲1:定义dp二、递推公式三、初始化四、循环顺序代码: 698. 划分为k个相等的子集题解…

通江县住房和城乡建设局网站个人网站导航html源码

Termius连接本地虚拟机与虚拟机快照 1. Termius连接本地虚拟机2. 虚拟机快照与还原2.1 设置快照以及恢复 附录 1. Termius连接本地虚拟机 ifconfig -a 查看配置 连接成功 2. 虚拟机快照与还原 在学习阶段我们无法避免的可能损坏Linux操作系统。 如果损坏的话,重新…

怎么看一个网站是谁做的单位网站建设意见建议

使用PCL的PCL的fromROSMsg()函数将ROS的sensor_msgs::PointCloud2类型数据转换为PCL的pcl::PointCloud<T>类型数据时&#xff0c;假如T只是PointXYZ没问题&#xff0c;假如是PointXYZI&#xff0c;intensity这个field的数据类型是float&#xff0c;但是数据长度就是不对的…

网站正在备案策点市场调研公司

Hydrooj nodejs版 HustOJ php版 QDUOJ python版 QDUOJ https://gitcode.com/QingdaoU/OnlineJudge/tree/master HustOJ hustoj: hustoj -- 流行的OJ系统&#xff0c;跨平台、易安装、有题库 Hydrooj https://hydro.js.org/docs/

中小企业微网站建设山东网站建设负面消息处理

查看自己系统的版本 必须运行 Windows 10 版本 2004 及更高版本&#xff08;内部版本 19041 及更高版本&#xff09;或 Windows 11 才能使用以下命令 在设置&#xff0c;系统里面就能看到 开启windows功能 直接winQ搜 开启hyber-V、使用于Linux的Windows子系统、虚拟机平…

北京网站建设网网站开发流程注意事项

前言 用户可使用小程序客服消息功能&#xff0c;与小程序的客服人员进行沟通。客服功能主要用于在小程序内 用户与客服直接沟通用&#xff0c;本篇介绍客服功能的基础开发以及进阶功能的使用&#xff0c;另外介绍多种客服的对接方式。 更多介绍请查看客服消息使用指南 客服视…