码迷,mamicode.com
首页 > 编程语言 > 详细

多线程 (二)

时间:2017-10-05 10:57:48      阅读:294      评论:0      收藏:0      [点我收藏+]

标签:ati   幸运   lag   static   判断   this   接口   改变   jpg   

1 线程的状态

技术分享

2 线程创建的第二种方式

package java012;

/**
 * 2017/9/29
 * 说明:
 */
class Fu{

}
class Demo extends Fu {
    private String name;
    public Demo(String name){
        this.name = name;
    }


    public void show(){
        for(int x =0;x<10;x++){
            System.out.println(name+":"+x+":线程的名称:"+Thread.currentThread().getName());
        }
    }

}
public class ThreadDemo {
    public static void main(String[] args) {
        
    }
}
  • 观察,上面的代码,我们知道,Demo的如果想将show()方法变为执行任务,那么Demo类必须继承Thread类,然后调用start()方法,很不幸运的是,java是单继承的,Demo类已经有父类Fu,那么现在貌似,我们只有将父类Fu,也继承Thread类,但是,如果我父类Fu并不想成为线程类,怎么办?那就是让Demo类实现一个接口,去扩展其功能,然后将需要执行的代码,放到run()方法中,看如下代码。
package java012;

/**
 * 2017/9/29
 * 说明:
 */
class Fu{

}
class Demo extends Fu implements Runnable {
    private String name;
    public Demo(String name){
        this.name = name;
    }


    public void show(){
        for(int x =0;x<10;x++){
            System.out.println(name+":"+x+":线程的名称:"+Thread.currentThread().getName());
        }
    }

    @Override
    public void run() {
        this.show();
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        
    }
}
  • 上述代码,好像已经可以实现多线程了,只要实例化Demo类,并调用start()方法就ok了。不,因为Demo类和Fu类都不是Thread类的子类,所以,我们怎么可以去调用start()方法,怎么办?我们直接实例化Thread类,然后调用start()方法。
package java012;

/**
 * 2017/9/29
 * 说明:
 */
class Fu{

}
class Demo extends Fu implements Runnable {
    private String name;
    public Demo(String name){
        this.name = name;
    }


    public void show(){
        for(int x =0;x<10;x++){
            System.out.println(name+":"+x+":线程的名称:"+Thread.currentThread().getName());
        }
    }

    @Override
    public void run() {
        this.show();
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread();
        Thread t2 = new Thread();
        t1.start();
        t2.start();
    }
}
  • 但是,我们知道,这样还是不行了,虽然开启了线程,但是运行的是Thread类中的run()方法里的代码,不是我自己需要运行的代码,怎么办?
public Thread(Runnable target)
  • 通过构造方法,将Runnable的子类作为参数传入进去。
package java012;

/**
 * 2017/9/29
 * 说明:
 */
class Fu{

}
class Demo extends Fu implements Runnable {


    public void show(){
        for(int x =0;x<10;x++){
            System.out.println(x+":线程的名称:"+Thread.currentThread().getName());
        }
    }

    @Override
    public void run() {
        this.show();
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();
        t2.start();
    }
}

技术分享

 

  • 创建线程的方式二的步骤
    • ①定义类实现Runnable接口。
    • ②重写接口中的run()方法,将线程的任务代码封装到run()方法中。
    • ③通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数传递进去。因为,线程的任务代码都封装在Runnable接口子类对象的run()方法中,所以要在线程对象创建的时候就明确要运行的任务。
    • ④调用线程对象的start()方法,开启线程。  

 

3 实现Runnable接口的好处

  • 将线程的任务从侠昵称的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务封装成对象。
  • 避免了java单继承的局限性。

 

4 多线程示例--卖票

package java012;

/**
 * 2017/9/30
 * 说明:
 */
class Ticket extends Thread {

    private int num = 100;

    public void sale(){
        while(true){
            if(num > 0){

                System.out.println(Thread.currentThread().getName()+":"+num--);
            }
        }
    }

    @Override
    public void run() {
        this.sale();
    }
}
public class TicketDemo {
    public static void main(String[] args) {

       Ticket t1 = new Ticket();
        Ticket t2 = new Ticket();
        Ticket t3 = new Ticket();
        Ticket t4 = new Ticket();


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

技术分享

  • 上面的代码是有问题的,因为每个线程内部都维护了100张票,和实际不符合。
package java012;

/**
 * 2017/9/30
 * 说明:
 */
class Ticket extends Thread {

    private static int num = 100;

    public void sale(){
        while(true){
            if(num > 0){

                System.out.println(Thread.currentThread().getName()+":"+num--);
            }
        }
    }

    @Override
    public void run() {
        this.sale();
    }
}
public class TicketDemo {
    public static void main(String[] args) {

       Ticket t1 = new Ticket();
        Ticket t2 = new Ticket();
        Ticket t3 = new Ticket();
        Ticket t4 = new Ticket();


        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
  • 这样好像是能实现我们的需求,但是如果我们的需求改变了,有2个线程卖100张票,2个线程卖另100张票,那么上面的代码就不行了,因为上面的代码是4个线程共享这100张票。
package java012;

/**
 * 2017/9/30
 * 说明:
 */
class Ticket implements Runnable {

    private  int num = 100;

    public void sale(){
        while(true){
            if(num > 0){

                System.out.println(Thread.currentThread().getName()+":"+num--);
            }
        }
    }

    @Override
    public void run() {
        this.sale();
    }
}
public class TicketDemo {
    public static void main(String[] args) {

      Ticket t = new Ticket();
      Ticket tt = new Ticket();

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

      Thread t3 = new Thread(tt);
      Thread t4 = new Thread(tt);

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

      t3.start();
      t4.start();

    }
}

 

5 多线程的安全

  • 多线程安全问题的产生现象
package java016;

class Ticket implements Runnable {
    private int num = 100;

    @Override
    public void run() {
        while(true) {
            if(num > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+num--);
            }
        }
    }
    
}

public class ThreadDemo {
    public static void main(String[] args) {
        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();
    }
}

技术分享

技术分享

 

5.1 线程安全问题产生的原因:

  • ①多个线程在操作共享的数据。
  • ②操作共享数据的线程代码有多条。  

 

5.2 如何解决多线程安全问题:

  • 就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算,必须等当前线程把这些任务代码执行完毕,方可,让其他线程去争抢CPU的执行权。  

 

  • 在java中,用同步代码块就可以解决这个问题。
synchronized(对象){
   //需要同步的代码块  
}
package java012;

/**
 * 2017/9/30
 * 说明:
 */
class Ticket implements Runnable {

    private  int num = 100;

    public void sale(){
        while(true){
            synchronized (this){
                if(num > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {

                    }
                    System.out.println(Thread.currentThread().getName()+":"+num--);
                }
            }
        }
    }

    @Override
    public void run() {
        this.sale();
    }
}
public class TicketDemo {
    public static void main(String[] args) {

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

    }
}

技术分享

技术分享

技术分享

 

5.3 同步的好处和弊端

  • 好处:解决了线程安全问题。
  • 弊端:当线程相当多的时候,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中就降低程序运行的效率。  

 

5.4 同步的前提

  • 多个线程
  • 多个线程使用的是同一个锁对象。

 

  • 示例:如果不是同一个锁对象,同步代码块不能解决多线程安全
package java012;

/**
 * 2017/9/30
 * 说明:
 */
class Ticket implements Runnable {

    private  int num = 100;

    public void sale(){
        Object obj = new Object();
        while(true){
            synchronized (obj){
                if(num > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {

                    }
                    System.out.println(Thread.currentThread().getName()+":"+num--);
                }
            }
        }
    }

    @Override
    public void run() {
        this.sale();
    }
}
public class TicketDemo {
    public static void main(String[] args) {

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

    }
}

技术分享

 技术分享

 技术分享

 

5.5 同步函数

  •  示例:有两个储户,去银行存钱,吗,每人存储3次,每次100元

 

package java012;

/**
 * 2017/10/4
 * 说明:
 */
class Bank{
    private  int sum ;
    public void add(int num){
        sum += num;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("存储了"+sum+"元");
    }

}
class Customer implements Runnable{
    private Bank b = new Bank();

    @Override
    public void run() {
        for(int x =0;x<3;x++){
            b.add(100);
        }
    }
}

public class BankDemo {
    public static void main(String[] args) {
        Customer c = new Customer();

        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);

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

    }
}

技术分享

 

  •  解决:使用同步代码块

 

package java012;

import java.util.concurrent.SynchronousQueue;

/**
 * 2017/10/4
 * 说明:
 */
class Bank{
    private  int sum ;
    private Object obj = new Object();
    public void add(int num){
        synchronized (obj){
            sum += num;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("存储了"+sum+"元");
        }
    }

}
class Customer implements Runnable{
    private Bank b = new Bank();

    @Override
    public void run() {
        for(int x =0;x<3;x++){
            b.add(100);
        }
    }
}

public class BankDemo {
    public static void main(String[] args) {
        Customer c = new Customer();

        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);

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

    }
}

技术分享

 

  •  不使用同步代码块,而使用同步函数

 

package java012;

import java.util.concurrent.SynchronousQueue;

/**
 * 2017/10/4
 * 说明:
 */
class Bank{
    private  int sum ;
    public synchronized void add(int num){
            sum += num;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("存储了"+sum+"元");
    }

}
class Customer implements Runnable{
    private Bank b = new Bank();

    @Override
    public void run() {
        for(int x =0;x<3;x++){
            b.add(100);
        }
    }
}

public class BankDemo {
    public static void main(String[] args) {
        Customer c = new Customer();

        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);

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

    }
}

技术分享

 

5.6 验证同步函数的锁

  • 思路:给一个多线程程序不同的标记(布尔值)切换,然后根据标记的不同,分别调用同步函数和同步代码块,如果不出现线程问题,即可证明同步函数的锁(验证同步函数的额锁是否是this)。

 

  • 示例:
package java012;

/**
 * 2017/10/5
 * 说明:
 */
class Ticket implements Runnable{
    private int num = 100;
    boolean flag = true;

    @Override
    public void run() {
        if(flag){
            while (true){
                synchronized (this){
                    if(num >0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+":"+num--);
                    }
                }
            }
        }else{
            while(true){
                sale();
            }
        }
    }

    private synchronized  void sale() {
        if(num >0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+num--);
        }
    }
}
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();

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


        t1.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.flag = false;
        t2.start();


    }
}

技术分享

技术分享

技术分享

 

5.7 同步函数和同步代码块的区别?

  • 同步函数的锁是固定的this。
  • 同步代码块的锁是任意的对象。

 

5.8 验证静态同步函数的锁

  • 验证静态同步函数的锁是否是该函数所属的字节码文件对象,即类名.class。
  • 思路:静态函数不可以通过this来访问,从加载时机上看,this是在类加载之后,所以推测静态同步函数的锁不是this,而是该函数所属的字节码文件对象,即类名.class。

 

  • 示例:
package java012;

/**
 * 2017/10/5
 * 说明:
 */
class Ticket implements Runnable{
    private static int num = 100;
    boolean flag = true;

    @Override
    public void run() {
        if(flag){
            while (true){
                synchronized (Ticket.class){
                    if(num >0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+":"+num--);
                    }
                }
            }
        }else{
            while(true){
                sale();
            }
        }
    }

    private synchronized static void sale() {
        if(num >0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+num--);
        }
    }
}
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();

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


        t1.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.flag = false;
        t2.start();


    }
}

技术分享

技术分享

技术分享

 

5.9 单例模式涉及的多线程问题

  • 单例设计模式有懒汉式和饿汉式。

 

  • 示例:单例设计模式之饿汉式
class Singleton{
    private static Singleton singleton = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return singleton;
    }


}
  • 示例:单例设计模式之懒汉式
class Singleton{
    private static Singleton singleton = null;

    private Singleton(){}

    public static Singleton getInstance(){
            if(singleton == null){
                singleton = new Singleton();
        }
        return singleton;
    }


}

 

 

  • 从上面的示例(懒汉和饿汉)中,我们可以看出懒汉式在多线程条件下是有线程安全的,因为有多条语句去操作共享数据。

 

  • 示例:多线程下的懒汉式
class Singleton{
    private static Singleton singleton = null;

    private Singleton(){}

    public static Singleton getInstance(){
        synchronized (Singleton.class){
            if(singleton == null){
                singleton = new Singleton();
            }
        }
        return singleton;
    }


}

 

6 死锁

  • 示例:
package java012;

/**
 * 2017/10/5
 * 说明:
 */
class Ticket implements Runnable{
    private  int num = 400;
    boolean flag = true;
    private Object obj = new Object();

    @Override
    public void run() {
        if(flag){
            while (true){
                synchronized (obj){
                    sale();
                }
            }
        }else{
            while(true){
                sale();
            }
        }
    }

    private synchronized  void sale() {
        synchronized (obj){
            if(num >0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+num--);
            }
        }

    }
}
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();

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


        t1.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.flag = false;
        t2.start();


    }
}

技术分享

技术分享

技术分享

技术分享

技术分享

 

多线程 (二)

标签:ati   幸运   lag   static   判断   this   接口   改变   jpg   

原文地址:http://www.cnblogs.com/xuweiweiwoaini/p/7613508.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!