JAVA_Lesson13(传智播客笔记之多线程)

多线程的状态


创建线程的第一种方式—继承Thread类(其实就是覆盖run方法,然后调用自己的show方法)

创建线程的第二种方式—实现Runnable接口

      但是人家万一已经有爹了,那就没法了。不支持多继承。此时要拓展该类的功能,创建接口。让其中的内容作为线程的任务执行。接口就是为了实现拓展功能;一个类在继承一个类的同时,也可以实现多个接口,为了这个类增加多个功能。通过接口的形式完成(拓展Demo类的功能,让其中的内容可以作为线程的任务执行。)

Class Demo implements Runnable
{
   Public void run()
   {
     Show();
   }//run()方法是Runnable里面就有的,而    show()是自己写的。
   Public void show()
   {
   }
}
Class ThreadDemo
{
    Public static void main()
   {
      Demo d=new Demo();//尽量跟下面2个对象结合起来。
加一条这个语句就可以了。(JAVA的API里面有)
       Thread t1=new Thread(d);
       Thread t2=new Thread(d);
       T1.start();
       T2.start();
    }
}

1. 定义类实现Runnable接口。

2. 覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3. 通过Thread类创建线程对象,并将Runnable接口子类对象作为Thread类的构造函数的参数进行传递。为什么?

因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。

4. 调用线程对象的start方法开启线程。

第二种方式的细节(2种方式多线程的原理弄懂)

第二种方式的好处

把线程任务封装成了对象(直接就将线程任务封装,不用线程出现,不用继承)其实本质目的:就是为了运行那一小团代码。

接口(额外功能)

实现Runnable接口的好处:

1. 将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务进行封装成对象。

2. 避免Java单继承的局限性。

所以,创建线程的第二种方式较为常用。

线程安全问题的现象(卖票的例子那集没有看)

因为cpu切换不确定性来来的安全隐患。

SleepJava.lang包里面有)

原因:1.多个线程操作共享数据。

2.操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程也参与了运算,就会导致问题出现。

同步代码

解决思路:

就是将多条操作共享数据的线程代码封装起来,当有线程执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码执行完,其他线程才可以参与运算。

Java中用Synchronized(同步代码块)可以解决这个问题。

格式是:Synchronized对象

{

需要被同步的代码;

}

对象该放什么呢?

自己造一个也行,放Object对象也行。

同步的前提

必须有多个线程并使用同一个锁。

同步函数

/*

需求:储户,两个,每个都到银行存钱,每次存100,共存3次。

*/

Class Bank
{
    Private int sum;
    Public void add(int num)
    {
        Sum+=num;
        Sop(“sum”+=sum);
    }
}
Class Cus implements Runnable
{
    Public void run()
    {
        Bank b=new bank();//需要把这一句放到run方法外面,并且加上private修饰。
        For(int x=0;x<3;x++)
        {
            B.add(100);
        }
    }
}
Class BankDemo
{
    Public static void main(String[] args)
    {
        Cus c=new Cus();
        Thread t1=new Thread(c);
        Thread t2=new Thread(c);
        T1.start();
        T2.start();
    }
}
上述运行结果为:

显然,银行的总金额不能像上述这样变化。

同步代码块和同步函数都能解决线程的安全问题。同步代码块有锁,但是同步函数的锁到底是谁呢?

验证同步函数的锁(听的还是有点模糊)

同步函数的锁是this(这一集的代码就是为了验证锁是this

同步函数和同步代码块的区别:

同步函数的锁是固定的this,同步代码块是任意的对象。建议使用同步代码块,同步函数可以作为同步代码块的简写形式。简写的话就会有他的弊端。

验证静态同步函数的锁

它的锁(对象)肯定不是this,静态函数也没有this所属啊。那么它的锁到底是哪个呢?

静态函数的同步锁使用的锁是:该函数所属字节码文件对象,可以用getclass方法获取,也可以当前类名.class表示

其实只需要保证多个线程用的是同一个锁就行。

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

多线程下的单例:

饿汉式
Class Single
{
    Private static final Single s=new Single();
    Private Single(){}
    Public static Single getInstance()
    {
        Return s;
    }
}

懒汉式
Class Single
{
    Private static Single s=null;
    Private Single(){}
    Public static synchronized Single getInstance()
    {
        If(s==null)
        s=new Single();
        Return s;
    }
} //会导致对象的不唯一,要解决问题,可以用同步函数解决问题,但是降低了效率,每次需要判断是不是同一个锁。可以考虑用同步代码块来解决:
Class Single
{
    Private static Single s=null;
    Private Single(){}
    Public static synchronized Single getInstance()
    {
        If(s==null)
        {
            Synchronized(Single.class)//this和Single.getclass都不行,因为静态方法不存在this,另外getclass方法是非静态的。而且还不用判断锁,但是这样效率就提高了。(设计java的人真心聪明啊!)加锁是为了解决安全问题,而if是为了解决效率问题。(面试多用这个)
            {
                If(s==null)
                s=new Single();
                Return s;
            }
        }
    }
}

死锁

程序实例来演示。

我的锁里面有你的锁,你的锁里面有我的锁。常见情景之一是同步嵌套。

Class Test
{
  Private boolean flag;
  Test(boolean flag)
  {
     This.flag=flag;
  }
  Public void run()
  {
    If(flag)
    {
      Synchronized(MyLock.locka)
      {	
        Sop(Thread.currentThread().getname()+“ if locka”);
        Synchronized(MyLock.lockb)
       {	
            Sop(Thread.currentThread().getname()+“ if lockb”);
        }
      }
   }
   Else
  {
     Synchronized(MyLock.lockb)
    {
        Sop(Thread.currentThread().getname()+“ else lockb”);
        Synchronized(MyLock.locka)
        {	
            Sop(Thread.currentThread().getname()+“ else locka”);
        }
    }
  }
 }
}
Class MyLock
{
    Public static final Object locka=new Object();
    Public static final Object lockb=new Object();
}

Class DeadLockTest
{
    Public static void main(String[] args)
    {
        Test a=new Test(true);
        Test b=new Test(false);
        Thread t1=new Thread(a);
        Thread t2=new Thread(a);
        T1.start();
        T2.start();
    }
}

 虽然flag2个线程各自分别有一份,但是因为flagboolean类型的变量,只有2种取值也行,若是int类型肯定不行。(还是有点不太理解暂时)

答疑

要玩同步,一定要使用同一个锁。

猜你喜欢

转载自blog.csdn.net/wearlee/article/details/80886521