java基础-多线程(一)

进程,正在执行中的程序,比如,windows里任务管理器,里面的每一个就是一个进程,cpu在某个时间,只能执行一个程序,cpu在快速的切换,才导致各个程序在同时执行,

线程,是进程中的内容,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫控制单元。

进程:正在进行中的程序。只要启动,就会在内存空间中分配一块空间,而进程就是用于标识这个空间的,它用于封装里面的控制单元,其实进程就是一个应用程序运行时的内存分配空间。

线程:线程才是进程中执行的部分,就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程,控制单元。

java VM ,启动的时候会有一个进程java.exe

该进程中至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程,目前用的是单线程

扩展,虚拟机启动的时候就是多线程,因为除了主线程还有一个线程在执行垃圾回收,

 

如何自定一个线程,

通过对api的查找,java已经提供了对线程这类事务的描述,就Thread类

创建线程的第一种方式,继承Thread 类,重写run方法

步骤:

1、定义类 ,继承Thread

2、复写Thread类中的run方法,目的 将自定义的代码存储在在run方法,让线程运行

3、调用线程的start的方法,该方法有两个作用,启动线程,调用run方法,开启线程并执行该线程的run方法

多线程在抢cpu资源,双核的cpu,内存就会成为瓶颈。我们会发现运行结果每一次都不同,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)。cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权,这就是多线程的一个特性,随机性,谁抢到,谁执行,执行多长时间cpu说的算。

为什么要覆盖run方法呢

Thread类用户描述线程,该类就定义一个功能,用于存储线程要运行的代码,该储存功能就是run方法,也就是说,Thread类中run方法是存储 线程要运行的代码,

主线程存储在main方法中

class Demo extend Thread{

public void run(){
    for(int i=0;i<60;i++){
    System.out.print("demo run---"+i);
    }

}

public main static void(String[] args){
    
    Demo d = new Demo();
    d.start();//开启线程并执行该线程的run方法
    d.run();//仅仅是对象调用方法,而线程创建了,并没有执行

    for(int x=0;x<60;x++){
        System.out.print("Hello world"+x);
    }

}

}

线程状态:

被创建:start()

运行:具备执行资格,同时具备执行权;

冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;

临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;

消亡:stop()  或者run 方法里结束了

 

static Thread currentThread();获取当前线程对象

getName(); 获取线程名称

Thread.currentThread.getName();

设置线程名称,setName或者构造函数

售票的例子

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

步骤:

1、定义类实现Runnable接口

2、覆盖Runnable接口中的run方法,

     将线程要运行的代码存放在该run方法中

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

4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

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

    为什么要将Runnable接口的子类对象传递给Thread类的构造函数

因为,自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定对象的run方法,就必须要明确该run方法所属的对象。

 

实现方式和 继承方式的区别是什么

实现方式好处,避免了单继承的局限性,在定义线程时,建立使用实现方式

继承的话,继承了 Thread类,就不能再继承其他的类了,因为java是单继承的

两种方式区别:

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

实现Runnable,线程代码存放在接口的子类的run方法中

class Ticket implements Runnable
{

    private int tick=100;
    
    public void run(){

    while(true){
    if(tick>0)
        System.out.print("Thread.currentThread().getName()"+"---sale"+tick--);
    }

   }


}


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


}

多线程的运行出现了安全问题

问题产生的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一线程参与进来执行,导致共享数据的错误。如果一个线程进来,把这个多条语句都执行完,在让下一个线程执行,就不会出现这个问题了。

解决办法就是, 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程都不可以参与执行。

 

java对于多线程的安全问题提供了专业的解决方式,就是同步代码块

synchronized(对象)

{

需要被同步的代码

}

哪些语句操作共享数据,哪些就是需要被同步的代码

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.print("Thread.currentThread().getName()"+"---sale"+tick--);
        }
        
        }

    }

   }


}


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


}

入参的对象如同锁,持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

同步的前提:

1、必须有两个或者两个以上的线程;

2、必须是多个线程使用同一个锁,

3、必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题

弊端:多个线程需要判断锁,较为消耗了资源,相对降低性能,因为判断锁需要消耗资源,产生了死锁

举一个多线程的例子

是否有安全问题,

如何找问题:

1、明确哪些代码是多线程运行代码

2、明确共享数据,一般成员变量都是

3、明确多线程运行代码中哪些语句是操作共享数据的

class Bank
{
private int sum;
Object obj =new Object();
public void add (int n)
	{    
        synchronized(obj)
         {
		 sum=sum+n;
		 try(Thread.sleep(10);)catch(Exception e){}
	     System.out.print("sum="+sum);
           }
	}
}

class Cus implements Runnable
{
	private Bank b= new Bank();
	
	public void run (){
		for(intx=0;x<3;x++){
			b.add(100);
			}
	
	}
	
}

class BankDemo
{
	Cus c= new Cus();
	Thread t1= new Thread(c);
	Thread t2=new  Thread(c);
	
	t1.start();
	t2.start();
	
	
}

同步有两种表现形式,同步代码块和同步函数把修饰符放到函数上,synchronized

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.print("sum="+sum);
      //  }
	}
}

同步函数用的是哪一个锁呢

函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this

class Ticket implements Runnable
{

    private int tick=100;
    
   // Object obj = new Object();
    public  void run(){
    
    while(true){

   // synchronized(obj){
        this.show();
        
      //}

    }

   }
    
    public synchronized void show(){
    if(tick>0)
        {
        try{Thread.sleep(10);}catch(Exception e){}
        System.out.print("Thread.currentThread().getName()"+"---show"+tick--);
        }
    
    }

}


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


}

不加上static修饰词,在堆内存是共享数据,加上static修饰词后,是在方法区中是共享数据。

如果同步函数被静态修饰后,使用的锁,不在是this了。因为静态方法中,不可以定义this。

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

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

class Ticket implements Runnable
{

    private int tick=100;
    
   // Object obj = new Object();
    public void run(){
    
    while(true){

   // synchronized(obj){
        this.show();
        
      //}

    }

   }
    
    public static synchronized void show(){
    if(tick>0)
        {
        try{Thread.sleep(10);}catch(Exception e){}
        System.out.print("Thread.currentThread().getName()"+"---show"+tick--);
        }
    
    }

}


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


}

通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁

当同步函数被static修饰时,这时的同步用的是哪个锁呢?

静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象

所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。

这个对象就是 类名.class

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

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

同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象

在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些

 

单例设计模式

饿汉式,使用的时候一般都是用饿汉式,但是面试的时候都是考的懒汉式

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  Single getInstance(){
        if(s==null){
          synchronized(Single.class){
           if(s==null)
            s= new Single(); 
          }  
         }    
      return s;
    }

}

用双重判断,提高了懒汉式的效率,

饿汉式和懒汉式有什么不同,

懒汉式的特点是实例的延迟加载,懒汉式延迟加载有没有问题?有,如果多线程访问时,会出现安全问题,可以加同步来解决,可以用同步代码块和同步函数都可以解决,稍微有些低效,用双重判断的方式解决这个低效问题。同步代码块的锁是什么,应为使用的static修饰,所以锁是,该类所属的字节码文件,类名.class   Single.class。

死锁,用生活例子,例如使用筷子,你有一跟筷子,我有一跟筷子,你要我的筷子,我要你的筷子,互相争抢,就卡这了,谁也用不了。

同步中嵌套同步,而锁却不同。举例子,例如一个线程拿这A锁想进B里,另一个线程拿着B锁,想进A里面。

class Test implement Runnable
{
    private boolean flag;
    Test(boolean flag){
        this.flag=flag;
        
    }
    
    public void run()
    {
        if(falg)
        {
            synchronized(MyLock.locka)
             {    
                System.out.print("if locka");
                synchronized(MyLock.lockb)
                {
                System.out.print("if lockb");
                }
            }
            
        }else
        {
             synchronized(MyLock.lockb)
             {    
                System.out.print("else lockb");
                synchronized(Mylock.locka)
                {
                System.out.print("else locka");
                }
            }
        }
        
    
    }
}

class MyLock
{
   static Object locka = new Object();
   static Object lockb = new Object();


}


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

        }


}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NeilNiu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值