Java中多线程原理 线程安全 线程同步 Lock锁 线程的六种状态 D190329

Java中多线程原理 线程安全 线程同步 Lock锁 线程的六种状态

今日概念性知识较多,但均为日后操作的重点,加深理解。

01.第一章:多线程_回顾实现线程的方式一:
1).定义一个线程类,继承自Thread,重写run()方法;

	class MyThread extends Thread{
		public void run(){
			...
		}
	}

2).启动线程
–|创建我们定义线程类的对象;
–|调用对象的start()方法启动线程;

	main(){
		MyThread t = new MyThread();
		t.start();
	//	t.run();//只是普通的方法调用
	}

02.第一章:多线程_多线程的运行原理:
在这里插入图片描述
03.第一章:多线程_Thread类的常用方法_构造方法:
1).构造方法:
–|Thread():无参构造方法
–|Thread(String name):使用一个线程名称构造一个线程对象
每个线程都有一个默认的线程名:Thread-索引值
通过getName()方法可以获取线程名称
–|Thread(Runnable run):使用一个Runnable的实现类构造一个线程对象;
在“创建线程的方式二”中演示
–|Thread(Runnable run,String name):使用一个Runnable和一个线程名称构造一个线程对象。

03.第一章:多线程_Thread类的常用方法_成员方法:

1).public String getName():获取线程的名称;
2).public static void sleep(long millis):让当前线程休眠指定的毫秒值;

public class MyThread extends Thread {
    @Override
    public void run() {
        for (; ; ) {
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String str = sdf.format(date);
            System.out.println(str);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3).public static Thread currentThread():获取当前的线程对象;

public class Demo {
    public static void main(String[] args) {
        //主线程也是一个线程,是由JVM来启动的
        //主线程也有线程名
	  //Thread.currentThread()可以获取当前的线程对象,然后调用getName()就能获取线程名称
        for (int k = 0; k < 100; k++) {
            System.out.println(Thread.currentThread().getName() + " k = " + k);

        }
    }
}

04.第一章:多线程_制作线程的方式二_Runnable接口:

1).定义一个子类,实现Runnable接口,重写run()方法;

/*
    注意:实现Runnable后,我们的类--不是一个线程类
         后期必须要委托给一个线程类Thread去执行
 */
public class MyRun implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " i = " + i);

        }
    }
}

2).启动线程:
1).创建一个自定义Runnable子类对象;
2).创建一个Thread对象,并将自定义对象作为参数传给Thread的构造方法;
3).调用Thread的start()方法启动线程。它内部会去调用我们自定义对象内部的run()方法。
示例代码:

public class Demo {
    public static void main(String[] args) {
        MyRun myRun = new MyRun();

        Thread t1 = new Thread(myRun,"章子怡");
        Thread t2 = new Thread(myRun,"汪峰");

        t1.start();
        t2.start();

    }
}

05.第一章:多线程_两种实现方式的区别:
1).第一种:继承自Thread.由于Java单继承的限制,对于子类形成了制约。
2).第二种:实现接口Runnable。子类可以同时实现多个接口【建议使用】

06.第一章:匿名内部类的方式实现线程:
1).匿名内部类的格式:

	new 父类/父接口名(){
		//子类的类体
	}

2).实现线程的方式一,使用匿名内部类的方式:
new Thread(){//Thread的子类类体}.start();
示例代码:

public static void main(String[] args) {
    //Thread t = new Thread的匿名子类对象();
    new Thread(){
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("i = " + i);
            }
        }
    }.start();

    //主线程继续
    for (int k = 0; k < 100; k++) {
        System.out.println("k = " + k);
    }
}

2).实现线程的方式二,使用匿名内部类的方式:
new Thread(Runnable子类对象).start();
示例代码:

new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("i = " + i);
        }
    }
}).start();

07.第一章:多线程安全性问题:
1).多线程安全性问题产生的原因:多个线程同时访问同一个变量;
2).示意图:
在这里插入图片描述
3).代码:
1.定义一个类,实现Runnable接口:

/*
    注意:实现Runnable后,我们的类--不是一个线程类
         后期必须要委托给一个线程类Thread去执行
 */
public class MyRun implements Runnable {
    int a = 100;
    @Override
    public void run() {//线程1,线程2
        for (int i = 0; i < 100; i++) {//线程1(1,2)//线程2(1,2,3)//线程1(3)
           a++;//1.将a的值100取出到运算器中;2.在运算器中计算 100 + 1 ;3.将101赋给a
        }
        System.out.println("线程执行完毕!");
    }
}

2.测试类:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyRun myRun = new MyRun();

        Thread t1 = new Thread(myRun,"章子怡");
        Thread t2 = new Thread(myRun,"汪峰");

        t1.start();
        t2.start();

        //主线程休息一秒,确保上面的两个线程执行完毕
        Thread.sleep(1000);

        //获取最终的结果
        System.out.println(myRun.a);//正常:300

    }
}

08.第二章:多线程售票模拟线程安全性问题:
1).定义Tickets类:

public class Tickets implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        //死循环
        while (true){
            //判断是否有票
            if (tickets > 0) {
                //取一张
                int t = tickets;
                try {
                    Thread.sleep(1);//目的:让效果更明显,让其它线程运行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tickets--;

                System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);
            }else{
                System.out.println("没票了!");
                break;
            }
        }
    }
}

2).定义测试类:

public class Demo {
    public static void main(String[] args) {
        //1.创建一个Tickets对象
        Tickets t = new Tickets();

        //2.创建两个线程
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");

        t1.start();
        t2.start();

    }
}

09.第二章:同步代码块_解决多线程售票线程安全性问题:
1).格式:
synchronized(锁对象){
//同步代码
}
锁对象:可以是任何对象,但必须保证多个线程共同使用同一个锁对象。
2).示例代码:
1).修改Tickets类:

public class Tickets implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj){
                if (tickets > 0) {
                    int t = tickets;
                    try {
                        Thread.sleep(1);//目的:让效果更明显
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;

                    System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);
                }else{
                    System.out.println("没票了!");
                    break;
                }
            }//一个线程执行完毕,会自动释放锁

        }
    }
}

2).主线程类:

public class Demo {
    public static void main(String[] args) {
        //1.创建一个Tickets对象
        Tickets t = new Tickets();

        //2.创建两个线程
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");

        t1.start();
        t2.start();

    }
}

10.第二章:同步方法_解决多线程售票线程安全性问题【常用】:
1).格式:
//同步方法,使用的锁对象是:this
public synchronized int getTicket(){
//同步代码
}
2).示例代码:
修改Tickets类即可:

public class Tickets implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while (true){
            int t = this.getTicket();
            if (t > 0) {
                System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);

            }else{
                System.out.println("没票了!!");
                break;
            }
        }
    }
    //同步方法,使用的锁对象是:this
    public synchronized int getTicket() {
        if (tickets > 0) {
            int t = tickets;
            try {
                Thread.sleep(1);//目的:让效果更明显
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            return t;
        } else {
            return 0;
        }
    }
}

11.第二章:Lock锁_解决多线程售票线程安全性问题:
1).格式:

Lock l = new 某个实现类对象();
l.lock();//加锁 
try { 
	//同步代码
} finally { 
	l.unlock(); //解锁
} 

2).示例代码:

public class Tickets implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();//加锁
            try {
                if (tickets > 0) {
                    int t = tickets;
                    try {
                        Thread.sleep(1);//目的:让效果更明显
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;

                    System.out.println(Thread.currentThread().getName() + " 取走一张票:" + t);
                } else {
                    System.out.println("没票了!");
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();//解锁
            }
        }
    }
}

12.第三章:线程状态:
在这里插入图片描述
13.第三章:线程状态_无限等待_wait方法:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();

        new Thread(){
            @Override
            public void run() {
                synchronized (obj) {
                    for (int i = 0; i < 20; i++) {
                        System.out.println("i = " + i);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (i == 10) {
                            //让当前的线程挂起,等待被唤醒
                            try {
                                System.out.println("我开始等待...");
                                obj.wait();//会立即释放obj锁
                                System.out.println("我醒了,开始干活了....");
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }.start();
        //开启一个新线程
        new Thread(){
            @Override
            public void run() {
                //获取obj锁对象
                System.out.println("第二个线程开始执行...");
                synchronized (obj){
                    //唤醒所有在obj锁上等待线程.
                    System.out.println("第二个线程开始唤醒....");
                    obj.notifyAll();//唤醒后,
                }//唤醒后,会释放锁
            }
        }.start();
    }
}

14.第三章:线程状态_无限等待_包子铺案例:
在这里插入图片描述

=======================================================================
学习目标总结:
01.能够描述Java中多线程运行原理
每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈
02.能够使用继承类的方式创建多线程
class MyThread extends Thread{
public void run(){
//…
}
}
main(){
MyThread t = new MyThread();
t.start();
t.run();//普通的方法调用
t.start();//抛异常
}

03.能够使用实现接口的方式创建多线程
class MyRun implements Runnable{
public void run(){
//…
}
}
main(){
MyRun myRun = new MyRun();
Thread t = new Thread(myRun);
t.start();
}
04.能够说出实现接口方式的好处
1).解决单继承的限制。
05.能够解释安全问题的出现的原因
1).多个线程同时访问同一个变量;
06.能够使用同步代码块解决线程安全问题
synchronized(锁对象){
//同步代码
}
07.能够使用同步方法解决线程安全问题
public synchronized void show(){
//同步代码
}
08.能够说出线程6个状态的名称
1.新建
2.可运行
3.计时等待
4.锁阻塞
5.无限等待
6.被终止
09.能够理解等待唤醒案例

	public static void main(String[] args) throws InterruptedException {
    Object obj = new Object();

    new Thread(){
        @Override
        public void run() {
            synchronized (obj) {
                for (int i = 0; i < 20; i++) {
                    System.out.println("i = " + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (i == 10) {
                        //让当前的线程挂起,等待被唤醒
                        try {
                            System.out.println("我开始等待...");
                            obj.wait();//会立即释放obj锁
                            System.out.println("我醒了,开始干活了....");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }.start();
    //开启一个新线程
    new Thread(){
        @Override
        public void run() {
            //获取obj锁对象
            System.out.println("第二个线程开始执行...");
            synchronized (obj){
                //唤醒所有在obj锁上等待线程.
                System.out.println("第二个线程开始唤醒....");
                obj.notifyAll();//唤醒后,
            }//唤醒后,会释放锁
        }
    }.start();
}

猜你喜欢

转载自blog.csdn.net/xc965746550/article/details/88903553