Java基础之线程初接触

1.线程的概述

1.进程的定义:一个正在执行中的程序 每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

2.线程的定义:进程中的一个独立的控制单元,线程控制着进程的执行。

3.进程中至少有一个线程(且java中该线程的代码存在于main方法中,因此称其为主线程)

4.使用多线程的意义:
-实现同时运行的目的
-提高下载效率[如下载工具某雷]

5.一个新的线程就是一个新的执行路径,CPU快速不停地在各个线程间进行切换(表现为各线程抢夺执行权),以达到看似同时运行的效果
多线程的一个特性:随机性


2.创建线程

1.继承Thread类:

步骤:
-定义一个类继承Thread
-复写Thread类中的run方法 ,run中放置需要在线程中运行的代码
-实例化该类,调用start方法[启动线程,调用run方法]

说明:若直接调用run方法,则没有启动线程。程序依旧是单线程运行

说明:在继承Thread的子类构造方法中注意super()的使用

示例代码:

//自定义线程类
class MyThread extends Thread{  
    public MyThread(String name){       
        super(name);//直接使用
    }
    //复写run方法
    public void run(){
        for(int i=0;i<50;i++){          
            System.out.println(this.getName()+" run"+i);
        }
    }
}


public class ThreadManager {

    public static void main(String[] args) {
        MyThread thread=new MyThread("新的线程");//新建线程
        thread.start();//启动线程       
    }

}

2.实现Runnable接口:

步骤:
- 定义类实现Runnable接口[implements关键字]
- 复写Runnable接口中的run方法
- 通过Thread类创建新的线程(将实现Runnable接口的子类对象作为实参传递给Thread的构造方法)
- 调用Thread中的start方法开启线程并调用Runnable接口子类中的run方法

编程思想:如果程序员只需要重写thread中的run方法,而不重写其他thread方法,应该实现runnable接口。因为除非程序员打算修改或增强类的基本行为,否则不应该为该类创建对象。

示例代码:

//实现Runnable的线程类
class MyNewThread implements Runnable{

    public void run() {      
        System.out.println("线程执行...");
    }

}

public class ThreadManagerByIm {

    public static void main(String[] args) {
        MyNewThread thread=new MyNewThread();
        Thread newThread=new Thread(thread);//以Runnable的实现类为实参实例化Thread
        newThread.start();//启动线程         
    }

}

3.实现Runnable方式与继承Thread方式创建线程的区别

  • 实现Runnable方式可以避免单继承的局限性[!important]
  • 实现Runnable方式使得对象中的资源可以被共享
  • 两种方式下线程代码的存放的位置有所不同

3.线程的状态

1.线程对象及名称

Thread currentThread()
返回对当前正在执行的线程对象的引用

String getName()
返回该线程的名称

void setName(String name)
改变线程名称,使之与参数 name 相同

2.线程状态一览

这里写图片描述

线程状态说明
新建线程新建
就绪线程运行start方法
运行线程获得CPU资源,即获得CPU执行权
阻塞线程处于等待或休眠状态,线程此时具有具备运行资格但没有执行权
死亡线程彻底结束

4.线程安全性问题

1.实例引入

//售票窗口类
class TicSale implements Runnable{
    private int ticket=100;//票数
    @Override
    public void run() {      
        while(true){
            if(ticket>0){
                try{
                    //令线程短暂休眠
                   Thread.sleep(10);    
                    }catch(Exception e){}                System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
            }
        }
    }

}

public class SaleTic {

    public static void main(String[] args) {
        TicSale ts=new TicSale();
        //用两个线程模拟双窗口售票
        Thread t1=new Thread(ts);
        Thread t2=new Thread(ts);
        t1.start();
        t2.start();
    }

}

运行结果:



Thread-0当前售出票号:2
Thread-1当前售出票号:2
Thread-0当前售出票号:1
Thread-1当前售出票号:1

在本例中,由于令售票线程短暂休眠,导致最终售票的结果出现异常情况。由此,可以体会到当多线程对共享数据进行操作时,存在一定的安全性问题。

2.出现安全性问题的原因

当多条语句在操作同一个共享数据时,一个线程对多条语句只执行了一部分(如中途进入休眠状态),另一个线程参与进来执行,就将导致共享数据的错误。

一个可行的解决方案:对多条操作共享数据的语句,只有当一个线程执行完,其他线程才能执行

3.多线程同步代码块

格式:

synchronized(被操作的共享资源所属的对象){
    //需要被同步的代码(操作共享数据的语句)
}

说明:需要被同步的对象一般可称为同步锁,一般为this 或是被操作的共享资源所属的对象(理论上任何对象都可以)。持有锁的线程可以在同步中执行,没有持有锁的线程即使获取了CPU的执行权,也无法进入同步代码块进行执行。

示例代码段:

public void run() {      
        while(true){
            synchronized(this){
                if(ticket>0){
                    try{
                        //令线程短暂休眠
                       Thread.sleep(10);    
                    }catch(Exception e){}
                    System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
                }
            }           
        }
    }

4.多线程同步方法

格式:在普通方法前面添加synchronized关键字(此时同步锁是this)

示例代码段:

public synchronized void sale(){
        if(ticket>0){
            try{
                //令线程短暂休眠
               Thread.sleep(10);    
            }catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"当前售出票号:"+ticket--);
        }
    }

5.进行同步的前提及弊端

前提:

  • 程序必须要有两个或两个以上的线程
  • 必须是多个线程使用同一个锁(尤其体现在同步方法与同步代码块中,因此将同步代码块的锁改为this

弊端:
- 多个线程需要判断锁,会消耗更多系统资源

6.分析多线程安全问题的方法:

  • 明确哪些代码是多线程运行代码(run中的代码及包含的方法)
  • 明确哪些是共享数据
  • 明确多线程运行代码中哪些语句是操作共享数据的

7.静态同步方法

说明:
如果同步方法被静态修饰后,使用的锁是该方法所在类的字节码文件对象[类名.class获取]
分析
静态方法进入内存时,内存中没有本类的实例对象。但是有该类对应的字节码文件对象。
通过类名.class获取 ,该对象的类型是Class

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值