当前位置:
首页 >
【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例
发布时间:2024/2/28
40
豆豆
生活随笔
收集整理的这篇文章主要介绍了
【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
Thread 的生命周期
一、实现Runnable接口方式
1、在 run 方法中使用 synchronized 块
/*** 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式* 1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题* 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。* 3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,* 其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。* 4.在Java中,我们通过同步机制,来解决线程的安全问题。* 方式一:同步代码块* synchronized(同步监视器){* //需要被同步的代码* }* 说明:* 1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。* 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。* 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。* 要求:多个线程必须要共用同一把锁。* 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。* 方式二:同步方法。* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。* 5.同步的方式,解决了线程的安全问题。---好处* 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 ---局限性*/ class TicketWindow implements Runnable {private int ticket = 100;// Object obj = new Object();//正确@Overridepublic void run() { // Object obj = new Object();//错误while (true) { // synchronized (obj){synchronized (this) {//此时的this:唯一的Window1的对象if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;} else {break;}}}} }public class Main1 {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow();Thread t1 = new Thread(ticketWindow);Thread t2 = new Thread(ticketWindow);Thread t3 = new Thread(ticketWindow);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }2、使用syncronized方法
/*** 使用同步方法解决实现Runnable接口的线程安全问题* 关于同步方法的总结:* 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。* 2. 非静态的同步方法,同步监视器是:this* 静态的同步方法,同步监视器是:当前类本身*/ class TicketWindow3 implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {show();}}private synchronized void show() {//相当于同步监视器是:this//synchronized (this){if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;}//}} }public class Main3 {public static void main(String[] args) {TicketWindow3 w = new TicketWindow3();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }二、继承Thread类方式
1、在 run 方法中使用 synchronized 块
/*** 使用同步代码块解决继承Thread类的方式的线程安全问题* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式* 说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。*/ class TicketWindow2 extends Thread {private static int ticket = 100;private static Object obj = new Object();@Overridepublic void run() {while (true) { // synchronized (obj){//正确的方式1:使用 static 的 obj 作为 synchronized 锁synchronized (TicketWindow2.class) {//正确的方式2:因为Class clazz = TicketWindow4.class, TicketWindow4.class只会加载一次 // synchronized (this){//错误的方式:this代表着t1,t2,t3三个对象if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + ":卖票,票号为:" + ticket);ticket--;} else {break;}}}} }public class Main2 {public static void main(String[] args) {TicketWindow2 t1 = new TicketWindow2();TicketWindow2 t2 = new TicketWindow2();TicketWindow2 t3 = new TicketWindow2();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }2、使用syncronized静态方法
/*** 使用同步方法处理继承Thread类的方式中的线程安全问题*/ class TicketWindow4 extends Thread {private static int ticket = 100;@Overridepublic void run() {while (true) {show();}}private static synchronized void show() { //正确:使用静态方法,相当于同步监视器:TicketWindow4.class,是当前类本身 // private synchronized void show(){ //错误:使用非静态方法,相当于同步监视器:t1,t2,t3,是三个不同的对象当做锁if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);ticket--;}} }public class Main4 {public static void main(String[] args) {TicketWindow4 t1 = new TicketWindow4();TicketWindow4 t2 = new TicketWindow4();TicketWindow4 t3 = new TicketWindow4();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }三、线程安全的懒汉式单例模式
/*** 使用同步机制,将单例模式中的'懒汉式'改写为线程安全的*/ public class Bank {private static Bank bankInstance = null;public static Bank getBankInstance() {if (null == bankInstance) {//进入synchronized前先判断,效率更高synchronized (Bank.class) {if (null == bankInstance) {bankInstance = new Bank();}}}return bankInstance;} }四、死锁问题示例
public class LockedTest {public static void main(String[] args) {StringBuffer sb1 = new StringBuffer();StringBuffer sb2 = new StringBuffer();new Thread() {@Overridepublic void run() {synchronized (sb1) {//拿到sb1锁sb1.append("a");sb2.append("b");System.out.println("拿到sb1锁");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("等待sb2锁");synchronized (sb2) {sb1.append('1');sb2.append('2');System.out.println("sb1=" + sb1);System.out.println("sb2=" + sb2);}}}}.start();new Thread(new Runnable() {//匿名Runnable对象,实现Runnable接口@Overridepublic void run() {synchronized (sb2) {System.out.println("拿到sb2锁");sb1.append("c");sb2.append("d");System.out.println("等待sb1锁");synchronized (sb1) {sb1.append('3');sb2.append('4');System.out.println("sb1=" + sb1);System.out.println("sb2=" + sb2);}}}}).start();} }输出
拿到sb1锁
拿到sb2锁
等待sb2锁
总结
以上是生活随笔为你收集整理的【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 【Python】多线程的使用,通过传参接
- 下一篇: 【Java】线程通信的例子:用两个线程打