线程、同步

目录

 

一、多线程

1.多线程的原理

2.线程类Thread的介绍

3.创建线程的两种方式

4.两种创建线程方式的区别

二、线程安全

1.线程安全问题出现的原因

2.线程同步

(1)同步代码块

目录

一、多线程

1.多线程的原理

2.线程类Thread的介绍

3.创建线程的两种方式

4.两种创建线程方式的区别

二、线程安全

1.线程安全问题出现的原因

2.线程同步

(1)同步代码块

(2)同步方法

(3)Lock锁

三、线程的状态

1.线程的六种状态

四、代码练习

1.卖包子案例

2.过山洞案例


 

(2)同步方法

(3)Lock锁

三、线程的状态

1.线程的六种状态

四、代码练习

1.卖包子案例


一、多线程

1.多线程的原理

同一时间内,CPU只能处理1条线程,只有1条线程在工作(执行);多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

程序启动运行main时候,java虚拟机启动一个进程,其中有一个主线程(main方法调用时候被创建)。随着调用Thread对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。

2.线程类Thread的介绍

构造方法:

public Thread() :分配一个新的线程对象。

public Thread(String name) :分配一个指定名字的新的线程对象。

public Thread(Runnable target) :分配一个带有指定目标新的线程对象。

public Thread(Runnable target,String name) :分配一个指定目标新的线程对象并指定名字。

常用方法:

public String getName() :获取当前线程名称。

public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。

public void run() :此线程要执行的任务在此处定义代码。

public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

3.创建线程的两种方式

方式一:

  描述:

将一个类声明为一个Thread的子类。

这个子类应该重写run类的方法,然后可以分配并启动子类的实例

步骤:

a.创建子类 继承 Threand (子类也是一个代表线程的类)

b.子类中重写run方法(编写任务代码的地方)

c.创建子类对象(创建一个线程对象)

d.启动线程,调用线程对象.start();

注意:

启动线程必须调用start,而不是调用run

方式二:

描述:

实现Runnable接口方式

步骤:

a.定义Runnable接口的实现类,并重写该接口的run()方法

b.用public Thread(Runnable target)的构造方法创建Thread对象

c.启动线程,调用线程对象.start();

4.两种创建线程方式的区别

实现Runnable接口比继承Thread类所具有的优势:

(1)可以避免java中的单继承的局限性。

(2)实现解耦操作,任务可以被多个线程共享,任务和线程独立。

(3)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

5.使用匿名内部类创建线程

使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法

二、线程安全

1.线程安全问题出现的原因

当有多个线程在同时运行,这些线程同时运行一段代码(同一段任务代码,同一个run方法),操作同一个共享数据时,这时候可能就会出现线程的安全问题,即线程不安全的。

2.线程同步

使线程按照一定的先后次序进行运行

(1)同步代码块

synchronized(同步锁){

需要同步操作的代码

}

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

①锁对象 可以是任意类型。

②多个线程对象 要使用同一把锁。

(2)同步方法

public synchronized void method(){

可能会产生线程安全问题的代码

}

对于非static方法,同步锁就是this。

对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

(3)Lock锁

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

Lock锁也称同步锁,加锁与释放锁方法化了,如下:

public void lock() :加同步锁。

public void unlock() :释放同步锁。

 

     Lock l = ...;

     l.lock();

     try {

         // access the resource protected by this lock

     } finally {

         l.unlock();

     }

 

三、线程的状态

1.线程的六种状态

(1)新建状态(new)

线程刚被创建,但是并未启动。还没调用start方法。

(2)可运行状态(Runnable)

线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。

(3)受阻塞状态(Blocked)

当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。

(4)限时等待状态(TimeWaiting)

同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep()、锁对象.wait()。

(5)无限等待状态(Waiting)

一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。

(6)消亡状态(Teminated)

因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

四、代码练习

1.卖包子案例

包子铺卖包子,有包子时则不生产,买家在没包子时不消费包子,有包子时消费包子。

public class BaoZi {
    boolean flag=false;//判断包子有没有
}
public class BaoZiTest {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    //如果包子没有了,调用wait()方法进入waiting状态
                    synchronized (bz) {
                        if (bz.flag == false) {
                            try {
                                bz.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("吃货醒来了");
                        System.out.println("吃货吃包子");
                        System.out.println("吃货吃完了...");
                        //包子吃完了,使flag=false并唤醒做包子线程
                        bz.flag = false;
                        bz.notify();
                    }
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {

                    synchronized (bz) {
                        //如果还有包子,调用wait()方法进入waiting状态
                        if (bz.flag == true) {
                            try {
                                bz.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("包子铺发现没包子了");
                        System.out.println("包子铺做包子");
                        System.out.println("包子已经上架了...");
                        //包子做出来了,使flag=true并唤醒吃包子线程
                        bz.flag = true;
                        bz.notify();
                    }
                }
            }
        });
        t2.start();
        System.out.println("hello");
    }
}

2.过山洞案例

请按要求编写多线程应用程序,模拟多个人通过一个山洞:
1.这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒;
2.随机生成10个人,同时准备过此山洞,并且定义一个变量用于记录通过隧道的人数。
显示每次通过山洞人的姓名,和通过顺序;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class GuoShanDong {
    ArrayList<String> names = new ArrayList<>();
    int a=1;

    public GuoShanDong() {
        Collections.addAll(names, "路飞", "索隆", "娜美", "乔巴", "香吉士",
                "乌索普", "弗兰奇", "甚平", "布鲁克", "罗宾");
    }

    public synchronized void guoShanDong() {
        Random r = new Random();
        if (names.size() > 0) {
            int i = r.nextInt(names.size());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println((a++)+"---"+Thread.currentThread().getName() + "---" + names.remove(i) + "正在通过山洞...");
        }
    }
}
public class Test09 {
    static int a = 1;

    public static void main(String[] args) {
        GuoShanDong gsd = new GuoShanDong();
        int i = 0;
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (gsd.a <= 10) {
                    gsd.guoShanDong();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (gsd.a <= 10) {
                    gsd.guoShanDong();
                }
            }
        }).start();
    }
}

猜你喜欢

转载自blog.csdn.net/fy_java1995/article/details/82024067
今日推荐