线程、同步

 

目录

一、多线程

1.多线程的原理

2.线程类Thread的介绍

3.创建线程的两种方式

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

二、线程安全

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

2.线程同步

(1)同步代码块

(2)同步方法

(3)Lock锁

三、线程的状态

1.线程的六种状态

四、代码练习

1.卖包子案例

2.过山洞案例


一、多线程

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();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值