Java基础(十四)多线程

 

 如何在自定义代码中定义一个线程?

 通过对API查找,创建一个线程方法一是继承自thread类。

具体步骤:

1、继承thread类

2、复写run ()方法

3、调用start()方法,此方法有两个作用,具体如下 :

每次运行结果都不同因为每个线程都在获取CPU使用权,单核情况下在某一时刻只 有一个线程在运行。谁抢到谁执行,所以结果是随机的。

为什么覆盖run方法?

thread类用于描述线程,该类定义了一个功能用于存储线程要运行的代码。该存储功能就是run()方法。

即run()方法用于存储线程中要运行的代码。

线程的几种状态:

常用方法:

static Thread currentThread();返回当前执行线程对象。

getName()  返回线程名称

setName(String name) 设置线程名称。(或者用构造函数也可以)

Thread类本身也实现了Runnable.接口

用这种方法避免了单继承的局限性。

1、创建一个类实现Runnable接口。

2、重写run方法。

3、通过Thread 类建立线程对象。

4、将Runnable接口的子类作为参数传给线程对象,使用Thread类的构造函数。

5、调用Thread类的start()方法开启线程并调用Runnable接口子类的run()方法。

继承和实现这两种方式建立线程有什么区别呢?

线程代码存在位置不同。

Thread继承:线程代码存在于子类的run方法中。

Runnable实现: 线程代码 存在于接口子类的run 方法中。

为什么要设置Runnable接口?

java只支持单继承,如果一个类中有一部分代码需要被多线程执行,但是它又已经有了父类,这时就要用到接口来解决这个问题。接口的好处之一就是扩展了类的功能。

多线程的安全问题。

下面是一个卖票的小程序。

/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
    private int tick = 100;

    public void run()//这里不能抛出,因为它是一个接口,其父类没有抛出异常,它也不能,只能try
    {
        while(true)
        {
            if(tick>0)
            {
               try{Thread.sleep(10); }//让正在执行的线程睡10秒。
               catch(Exception e){}
              System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
            }
        }
    }
}

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

由于 多个线程操作同一个变量,会导致一些问题,一个线程对多条语句只执行了一部分另一个线程参与进来 ,导致同享数据出问题。

解决方法:

同步代码块:对象就好比一个锁,持有锁的线程才可以运行,没持有锁的进程即使有CPU执行权也无法执行。

synchronized(对象)
{
    需要同步的代码(哪里操作共享数据哪里就是)
}

修改之后的代码:

/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
    private int tick = 100;

    Object obj = new Object();

    public void run()
    {
       while(true)
       {
          synchronized(obj)
          {
             if(tick>0)
             {
              try{Thread.sleep(10);}catch(Exception e){}
              System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
             }
          }
       }
    }
}

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

同步的前提:

1、至少两个以上的线程。

2、用的是同一个锁,保证同步中只有一个线程在执行。

优点:解决了多线程安全问题

缺点:多个线程都需要判断锁的状态,比较消耗资源。

同步代码块与函数都是封装了一部分代码,那他们的区别是?      ——同步代码块可以同步。

所以有了新的关键字:只需要把synchronized 作为关键字放在函数声明中即可,也不需要定义对象了。因为函数都是被对象调用的,都有个引用对象,this ,这次就是这个对象在调用。

但加了static 后的函数由于 没有了this,所以静态同步函数的锁 是??

静态进内存时,还没有本类创建的对象 ,但是有一个该类对应的字节码文件【类名.class】,该对象的类型是class。

静态同步方法使用的锁是该方法所在类的字节码文件对象(类名.class)

下例:

/**
需求:有两个客户向银行存钱,都存300,存3次每次100。
*/

class Bank
{
    private int sum;
   // Object obj = new Object();

    public synchronized void add(int n)
    {
        //synchronized(obj)
       // {
            sum=sum+n;

            try{Thread.sleep(10);}
            catch(Exception e){}

            System.out.println("sum="+sum);
        //}
    }
}

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

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

class BankTest
{
    public static void main(String[] args)
    {
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}

带有synchronized函数称为同步函数。但是放关键字的位置很重要,比如上面的卖票程序需要改成这个样子:

/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
    private int tick = 100;

    //如果在这里加关键字,那么只有一个线程执行这个程序,其它进不去。结果是一个线程卖100张票。
    public void run()
    {
        while(true)
        {
            show();
        }
    }
    //必须仅让涉及问题的代码封装成函数
    public synchronized void show()
    {
        if(tick>0)
            {
               try{Thread.sleep(10); }
               catch(Exception e){}
              System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
            }
    }
}

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

同步锁的重要应用:单例设计模式

/**
两种单例设计模式:
饿汉式

class Single
{
    private static final Single s = new Single();
    private Single();
    public static void getInstance()
    {
        return s;
    }
}


懒汉式(延迟加载)

class Single
{
    private static Singel s=null;

    private Single(){}

    public static synchronized void getInstence()
    {
        if (s==null)

            s=new Single();

        return s;
    }
}
*/
class Single
{
    private static Singel s=null;

    private Single(){}

    public static void getInstence()
    {
        if (s==null)
        {       
            synchronized (Single.class)
            {
                if(s==null)
                {
                    s=new Single();
                }
            }
        }
        return s;
    }
}

死锁:同步中嵌套同步,而锁却不同。

class Test implements Runnable
{
    private boolean flag;

    Test(boolean flag)
    {
        this.flag = flag;
    }

    public void run()
    {
        if(flag)
        {
            while(true)
            {
                synchronized(Mylock.locka)
                {
                    System.out.println("if loaka");
                    synchronized(Mylock.lockb)
                    {
                        System.out.println("if loakb");
                    }
                }
            }
        }
        else
        {
            while(true)
            {
                synchronized(Mylock.lockb)
                {
                    System.out.println("else loakb");
                    synchronized(Mylock.locka)
                    {
                        System.out.println("else loaka");
                    }
                }
            }
        }
    }
}

class Mylock
{
    static Object locka = new Object();
    static Object lockb = new Object();
}

class Death2
{
    public static void main(String[] args)
    {
        Thread t1 = new Thread(new Test(true));
        Thread t2 = new Thread(new Test(false));
        t1.start();
        t2.start();

    }
}

一种报错是:无效线程异常——多次开启一个线程。 start就是让线程从初始到运行状态。已经在运行状态的线程就不能再运行了

 这里一般用复写Runnable接口的方法。

下面这段代码有两个点值得学习:

1 由于input 和output 必须操作的是同一个对象,而用单例设计模式又太麻烦,这里用了一个传对象的方式。在input和output内自己定义了一个对象,我们只需要在外边定义一个对象然后传进来就搞定了。

2这里没用flag=falser 的方式 而是用了一个没见过的x=0  x=(x+1)%2

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纸城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值