【Java】【基础篇】day11:多线程

前言

本期任务:毕向东老师Java视频教程学习笔记(共计25天)


代码

/*
java多线程的几种实现方式
*/

public class ThreadTest {
    public static void main(String[] args) {
        // 新写法
        new Thread(() -> {
            for (int x = 0; x < 200; x++) {
                System.out.println(Thread.currentThread().getName() + "......" + x);
            }
        }).start();

        /*
        创建线程的第一种方式是:继承Thread类。原因:要覆盖run方法,定义线程要运行的代码。
        步骤:
            1,继承Thread类。
            2,覆盖run方法。将线程要运行的代码定义其中。
            3,创建Thread类的子类对象,其实就是在创建线程,调用start方法。
        */

        // 旧写法
        new Test1().start();


        // 匿名内部类
        new Thread() {
            public void run() {
                for (int x = 0; x < 200; x++) {
                    System.out.println(Thread.currentThread().getName() + "......" + x);
                }
            }
        }.start();


        // 实现Runable接口
        /*

        创建线程的第二种方式:实现Runnable接口。

        步骤:
            1,定义了实现Runnable接口。
            2,覆盖接口的run方法。将多线程要运行的代码存入其中。
            3,创建Thread类的对象(创建线程),并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
                为什么要传递?因为线程要运行的代码都在Runnable子类的run方法中存储。所以要将该run方法所属的对象
                传递给Thread。让Thread线程去使用该对象调用其run方法。
            4,调用Thread对象的start方法。开启线程。

        */

        Runnable r = new Runnable() {
            public void run() {
                for (int x = 0; x < 200; x++) {
                    System.out.println(Thread.currentThread().getName() + "......" + x);
                }
            }
        };
        new Thread(r).start();



        // 主线程运行
        for (int x = 0; x < 1000; x++) {
            System.out.println(Thread.currentThread().getName() + "......" + x);
        }

    }
}

class Test1 extends Thread {
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + "......" + x);
        }
    }
}
/*
需求:简单的卖票程序
多个窗口同时卖票

创建线程的第一种方式:继承Thread类
步骤:
1. 定义类继承Thread
2. 复写Thread类中的run方法
3. 调用线程的start方法
    该方法两个作用,启动线程,调用run方法

创建线程的第二种方式:实现Runable接口
步骤:
1. 定义类实现Runable接口
2. 覆盖Runable接口中的run方法
    将线程要运行的代码放在该run方法中

3. 通过Thread类建立线程对象
4. 将Runable接口的子类对象作为实际参数传递给Thread类的构造函数
    为什么要将Runable接口的子类对象传递给Thread的构造函数:
    因为,自定义的run方法所属的对象是Runable接口的子类对象
    所以要让线程去指定队形的run方法,就必须明确该run方法所属对象

5. 调用Thread类的start方法开启线程并调用Runable接口子类的run方法

实现方式与继承方式有什么区别呢?
    实现方式好处:避免了单继承的局限性
    在定义线程时,建议使用实现方式

两种方式区别:
    继承Thread:线程代码存放在Thread子类的run方法中
    实现Runable:线程代码存放在接口的子类的run方法中


使用Thread.sleep()制造线程不安全,使用这个方法,会抛出InterruptException异常
由于存在线程10ms的休眠时间,使得出现tick为0,甚至-1、-2的错票情形,多线程运行存在安全问题

问题原因:
    当多条语句在操作同一个线程共享数据的时候,一个线程对多条语句只执行了一部分,还没执行完,
    另一个线程参与进来执行,导致共享数据出现错误。

解决方法:
    对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。


Java对于多线程的安全问题提供了专业的解决方式

就是同步代码块

synchronized(对象){

    需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁

火车上的卫生间 --- 经典。

同步的前提:
1. 必须要有两个或两个以上的线程。
2. 必须是多个线程使用同一个锁
3. 必须保证同步中之恶能有一个线程在运行。

好处:解决了线程安全问题
弊端:多个线程需要判断锁,较为消耗资源

同步函数也是可以解决线程安全的,直接在函数中加入synchronized限制符

同步函数用的是哪个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this

通过程序进行验证
使用两个线程来买票
一个线程在同步代码块中
一个线程在同步函数中
都在执行买票动作

如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不是this,因为静态方法中不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象:类名.class


*/

// 实现Runable
class Ticket implements Runnable {
    private static int tick = 1000;
    Object obj = new Object();

    // 同步函数
    public synchronized void show() {
        if (tick > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
            System.out.println(Thread.currentThread().getName() + ":" + tick--);
        }
    }

    public void run1() {
        while (true)
            show();
    }

    // 同步代码块
    public void run() {

        while (true) {
            synchronized (this) {
                if (tick > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + tick--);
                }
            }
        }
    }
}


// 继承Thread
class Ticket1 extends Thread {
    private static int tick = 100;

    public void run() {
        while (tick > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + tick--);
        }
    }
}


public class TicketDemo {
    public static void main(String[] args) {
        // 实现Runable
        Ticket t = new Ticket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);

        t1.start();
        t2.start();
        t3.start();
        t4.start();


        /*
        // 继承Thread
        Ticket1 t1 = new Ticket1();
        Ticket1 t2 = new Ticket1();
        Ticket1 t3 = new Ticket1();
        Ticket1 t4 = new Ticket1();

        t1.start();
        t2.start();
        t3.start();
        t4.start();

        */
    }


}

/*
单例设计模式

1. 饿汉式
class Single{
    private static final Single s = new Single();
    private Single(){}
    public static Single getInstance(){
        return s;
    }
}

2. 懒汉式
    延迟加载,
    多线程访问时会出现线程安全问题,
    可使用同步锁解决, 同步函数和同步代码块都可,
    使用双重判断可解决效率问题,
    加同步的使用,使用的锁是该类所属的字节码文件对象

*/


class Single {
    private static Single s = null;

    private Single() {
    }

    public static Single getInstance() {
        if (s == null) {
            synchronized (Single.class) {
                if (s == null) {
                    s = new Single();
                }
            }

        }
        return s;
    }
}
/*
死锁
同步中嵌套同步。

以下为死锁的一个实例

*/

class Test implements Runnable {
    private boolean flag;

    Test(boolean flag) {
        this.flag = flag;
    }

    public void run() {
        if (flag) {
            synchronized (MyLock.locka) {
                System.out.println("if locka");
                synchronized (MyLock.lockb) {
                    System.out.println("if lockb");
                }
            }
        } else {
            synchronized (MyLock.lockb) {
                System.out.println("else lockb");
                synchronized (MyLock.locka) {
                    System.out.println("else locka");
                }
            }
        }
    }
}

class MyLock {
    static Object locka = new Object();
    static Object lockb = new Object();
}


public class DeadLockDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Test(true));
        Thread t2 = new Thread(new Test(false));
        t1.start();
        t2.start();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章