zoukankan      html  css  js  c++  java
  • 多线程2

    如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。


    其实,线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

    演示卖票多线程案例:
    package cn.itcast.selltickets;

    public class TheadDemo {

    public static void main(String[] args) {
    Ticket ticket = new Ticket();
    Thread t0 = new Thread(ticket);
    Thread t1 = new Thread(ticket);
    Thread t2 = new Thread(ticket);
    t0.start();
    t1.start();
    t2.start();
    }
    }

    package cn.itcast.selltickets;

    public class Ticket implements Runnable {


    private int num = 100;

    public void run() {
    while(true) {
    if (num > 0) {
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "出售第" + num--);
    }
    }
    }
    }


    Thread-0出售第100
    Thread-1出售第99
    Thread-2出售第100
    Thread-0出售第98
    Thread-1出售第97
    Thread-2出售第96
    Thread-0出售第95
    Thread-1出售第94
    Thread-2出售第93
    Thread-0出售第92
    Thread-2出售第91
    Thread-1出售第90
    Thread-0出售第88
    Thread-2出售第89
    Thread-1出售第87
    Thread-0出售第86
    Thread-1出售第85
    Thread-2出售第84
    Thread-0出售第83
    Thread-1出售第82
    Thread-2出售第81
    Thread-0出售第80
    Thread-2出售第79
    Thread-1出售第78
    Thread-0出售第77
    Thread-1出售第76
    Thread-2出售第75
    Thread-0出售第74
    Thread-2出售第73
    Thread-1出售第72
    Thread-0出售第71
    Thread-2出售第70
    Thread-1出售第69
    Thread-0出售第68
    Thread-1出售第67
    Thread-2出售第66
    Thread-0出售第65
    Thread-1出售第64
    Thread-2出售第63
    Thread-0出售第62
    Thread-2出售第61
    Thread-1出售第60
    Thread-0出售第59
    Thread-2出售第58
    Thread-1出售第57
    Thread-0出售第56
    Thread-1出售第55
    Thread-2出售第54
    Thread-0出售第53
    Thread-2出售第52
    Thread-1出售第51
    Thread-0出售第50
    Thread-1出售第49
    Thread-2出售第49
    Thread-0出售第48
    Thread-1出售第47
    Thread-2出售第46
    Thread-0出售第45
    Thread-2出售第44
    Thread-1出售第43
    Thread-0出售第42
    Thread-1出售第41
    Thread-2出售第40
    Thread-0出售第39
    Thread-2出售第38
    Thread-1出售第37
    Thread-0出售第36
    Thread-1出售第35
    Thread-2出售第34
    Thread-0出售第33
    Thread-2出售第32
    Thread-1出售第31
    Thread-0出售第30
    Thread-2出售第29
    Thread-1出售第28
    Thread-0出售第27
    Thread-2出售第26
    Thread-1出售第25
    Thread-0出售第24
    Thread-1出售第23
    Thread-2出售第22
    Thread-0出售第21
    Thread-2出售第20
    Thread-1出售第19
    Thread-0出售第18
    Thread-2出售第17
    Thread-1出售第16
    Thread-0出售第15
    Thread-2出售第14
    Thread-1出售第13
    Thread-0出售第12
    Thread-1出售第11
    Thread-2出售第10
    Thread-0出售第9
    Thread-2出售第8
    Thread-1出售第7
    Thread-0出售第6
    Thread-1出售第5
    Thread-2出售第4
    Thread-0出售第3
    Thread-1出售第2
    Thread-2出售第1
    Thread-0出售第0
    Thread-1出售第-1

    用同步代码块synchronized解决
    package cn.itcast.selltickets;

    public class Ticket implements Runnable {

    private int num = 100;

    private Object o = new Object();

    public void run() {
    while (true) {
    synchronized (o) {
    if (num > 0) {
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "出售第" + num--);
    }
    }
    }
    }
    }

    用同步方法解决
    package cn.itcast.selltickets;

    public class Ticket implements Runnable {

    private int num = 100;


    public synchronized void run() {
    while (true) {
    if (num > 0) {
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "出售第" + num--);
    }
    }
    }
    }

    java中提供了线程同步机制,它能够解决上述的线程安全问题。
    线程同步的方式有两种:
    ? 方式1:同步代码块
    ? 方式2:同步方法
    同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    1.2.2 同步方法
    ? 同步方法:在方法声明上加上synchronized
    public synchronized void method(){
    可能会产生线程安全问题的代码
    }
    同步方法中的锁对象是 this

    ? 静态同步方法: 在方法声明上加上static synchronized
    public static synchronized void method(){
    可能会产生线程安全问题的代码
    }
    静态同步方法中的锁对象是 类名.class


    用lock接口解决安全性
    package cn.itcast.selltickets;

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class Ticket implements Runnable {

    private int num = 100;


    private Lock lock = new ReentrantLock();
    public void run() {
    while (true) {
    lock.lock();
    if (num > 0) {
    try {
    Thread.sleep(100);
    System.out.println(Thread.currentThread().getName() + "出售第" + num--);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }finally {
    lock.unlock();

    }
    }

    }
    }
    }


    同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。


    死锁代码实现
    package cn.itcast.deadlock;

    public class DeadLock implements Runnable {

    private int i = 0;
    @Override
    public void run() {
    // TODO Auto-generated method stub
    while(true) {
    if (i % 2 == 0) {
    synchronized(LockA.locka){
    System.out.println("if...locka" + Thread.currentThread().getName());
    synchronized(LockB.lockb) {
    System.out.println("if...lockb" + Thread.currentThread().getName());
    }
    }
    }else {
    synchronized (LockB.lockb) {
    System.out.println("else...lockb" + Thread.currentThread().getName());
    synchronized (LockA.locka) {
    System.out.println("else...locka" + Thread.currentThread().getName());
    }
    }
    }
    //i=8,刚从if出来,正准备加1,这时cpu被另一个线程抢了,另一个想成走if,把A锁拿了,这时,cpu又被抢了另一个线程i++,走else,把B锁抢了
    //,这时拿到A锁的线程即使抢到cpu资源由于没有b锁,也阻塞了,另一个线程同样的.
    i++;
    }
    }

    }


    package cn.itcast.deadlock;

    public class Demo {

    public static void main(String[] args) {
    DeadLock dead = new DeadLock();
    Thread t0 = new Thread(dead);
    Thread t1 = new Thread(dead);
    t0.start();
    t1.start();
    }
    }


    package cn.itcast.deadlock;

    public class LockA {
    private LockA() {
    }

    public static final LockA locka = new LockA();
    }


    package cn.itcast.deadlock;

    public class LockB {

    private LockB(){}
    public static final LockB lockb = new LockB();
    }


    if...lockaThread-0
    if...lockbThread-0
    else...lockbThread-0
    else...lockaThread-0
    if...lockaThread-0
    if...lockbThread-0
    else...lockbThread-0
    else...lockaThread-0
    if...lockaThread-0
    if...lockbThread-0
    else...lockbThread-0
    if...lockaThread-1

    线程通信:
    package cn.itcast.threadnotify;

    public class Demo {

    public static void main(String[] args) {
    Resource r = new Resource();
    Output out = new Output(r);
    Input in = new Input(r);
    Thread t0 = new Thread(in);
    Thread t1 = new Thread(out);
    t0.start();
    t1.start();


    }
    }

    package cn.itcast.threadnotify;

    public class Input implements Runnable {

    private Resource r;

    public Input(Resource r) {
    this.r = r;
    }

    @Override
    public void run() {
    // TODO Auto-generated method stub
    int i = 0;
    while (true) {
    synchronized (r) {
    if (r.flag) {
    try {
    r.wait();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    if (i % 2 == 0) {
    r.name = "张三";
    r.sex = "男";
    } else {
    r.name = "lisi";
    r.sex = "nv";
    }
    //将对方线程唤醒,标记改为true
    r.flag = true;
    r.notify();
    }
    i++;
    }
    }

    }

    package cn.itcast.threadnotify;

    public class Output implements Runnable {

    private Resource r;

    public Output(Resource r) {
    this.r = r;
    }

    @Override
    public void run() {
    // TODO Auto-generated method stub

    while (true) {
    synchronized (r) {
    if (!r.flag) {
    try {
    r.wait();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    System.out.println(r.name + ".." + r.sex);
    r.flag = false;
    r.notify();
    }
    }
    }

    }

    package cn.itcast.threadnotify;

    public class Resource {

    public String name;
    public String sex;
    public boolean flag = true;

    }

    在开始讲解等待唤醒机制之前,有必要搞清一个概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

    等待唤醒机制所涉及到的方法:

    l  wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。

    l  notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。

    l  notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

    其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

    仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

    因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。

    l  同步锁

    多个线程想保证线程安全,必须要使用同一个锁对象

    n  同步代码块

             synchronized (锁对象){

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

    }

                               同步代码块的锁对象可以是任意的对象

     

    n  同步方法

             public synchronized void method()

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

    }

                               同步方法中的锁对象是 this

    n  静态同步方法

    public synchronized void method()

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

    }

    静态同步方法中的锁对象是 类名.class

     

    l  多线程有几种实现方案,分别是哪几种?

             a, 继承Thread类

             b, 实现Runnable接口

             c, 通过线程池,实现Callable接口

    l  同步有几种方式,分别是什么?

             a,同步代码块

             b,同步方法

               静态同步方法

    l  启动一个线程是run()还是start()?它们的区别?

             启动一个线程是start()

             区别:

                      start: 启动线程,并调用线程中的run()方法

                      run  : 执行该线程对象要执行的任务

    l  sleep()和wait()方法的区别

             sleep: 不释放锁对象, 释放CPU使用权

                               在休眠的时间内,不能唤醒

                      wait(): 释放锁对象, 释放CPU使用权

                               在等待的时间内,能唤醒

    l  为什么wait(),notify(),notifyAll()等方法都定义在Object类中

             锁对象可以是任意类型的对象

  • 相关阅读:
    Atitit.struts2体系结构大总结
    Atitit.hibernate体系结构大总结
    Atitit. 最佳实践 QA----降低cpu占有率--cpu占用太高怎么办
    Atitit.软件GUI按钮与仪表盘(01)--js区-----js格式化的使用
    Atitit.软件控件and仪表盘(23)--多媒体子系统--视频输出切换控制cvbs av s-video Ypbpr pal ntsc
    Atitit.软件开发概念说明--io系统区--特殊文件名称保存最佳实践文件名称编码...filenameEncode
    Atitit.软件按钮与仪表盘(13)--全文索引操作--db数据库子系统mssql2008
    Atitit.软件GUI按钮与仪表盘(01)--报警系统--
    Atitit.软件开发概念(11)--网络子系统--url编码 空格问题URLEncoder java js php
    Atitit.软件仪表盘(0)--软件的子系统体系说明
  • 原文地址:https://www.cnblogs.com/zjj1996/p/9108045.html
Copyright © 2011-2022 走看看