java多线程

疑问:windows号称是多任务的操作系统 , widnows真的在同时运行多个应用程序吗?

宏观角度,windows确实在同时运行多个应用程序。

微观角度, cpu在做一个快速的切换动作,由于切换的速度比较快,所以我们没有感觉到而已.

进程: 正在运行的程序称作为一个进程。 进程负责了内存空间的划分。

线程: 线程负责了代码的执行, 线程是进程中的一个代码执行路径。

多线程 : 在一个进程中有多个线程在执行不同的任务代码。

疑问: 以前我们没有学过线程啊,为什么代码还是可以执行呢?
任何一个java程序在运行的时候,jvm都会为该应用程序创建一个主线程, 主线程的任务就是把main方法的代码执行完毕。

笔试题目: 一个java应用程序在运行的时候至少有几个线程?
2个线程, 主线线程, 垃圾回收器线程。

多线程的好处:
1. 解决在一个进程中可以同时执行多个任务代码的问题。
2. 提高了资源利用率。

多线的缺点:
1. 增加了cpu的负担。
2. 降低了一个进程中线程的执行概率.
3. 引发了线程安全问题。
4. 引发了死锁现象.

如何自定义线程:
1. 自定义一个类继承Thread。
2. 重写Thread类的run方法, 把自定义线程的任务代码定义在run方法上。

    疑问: 重写run方法的目的是什么?
        每个线程都有自己的任务代码, main线程的任务代码是main方法里面的所有代码, 而自定义线程的任务代码就是run方法中的所有代码。
3. 创建自定义线程对象。   
4. 调用线程的start方法开启线程, 一个线程一旦开启就会执行run方法中的所有代码。

注意: run方法千万不能直接调用,直接调用run方法相当于调用了一个普通的方法而已,并没有开启一个新的线程。
public class Demo1 extends Thread {
    @Override
    public void run() {
        //自定义线程的任务代码
        for(int i  = 0 ; i< 100 ; i++){
            System.out.println("自定义线程:"+i);
        }
    }
    //每个线程都有自己的任务代码.....
    public static void main(String[] args) {
        //创建线程对象
        Demo1 d = new Demo1();
        //开启线程
        d.start();
        for(int i  = 0 ; i< 100 ; i++){
            System.out.println("主线程:"+i);
        }
    }
}

线程常用的方法:

 Thread(String name)     初始化线程的名字

 setName(String name)    设置线程对象名    
 getName()               返回线程的名字

 static sleep()                 那个线程执行了sleep的代码 ,那么该线程就会睡眠指定毫秒数。

 currentThread()        返回当前执行该方法的线程对象引用。

 getPriority()             返回当前线程对象的优先级   默认线程的优先级是5
 setPriority(int newPriority) 设置线程的优先级    虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。
public class Demo3 extends Thread { 
    public Demo3(String name){
        super(name);// 指定调用Thread类一个参数的构造方法。给线程初始化名字。
    }   
    @Override
    public void run() {
        for(int i = 0 ; i<100 ; i++){       System.out.println(Thread.currentThread().getName()+":"+i);
        }   //System.out.println(Thread.currentThread()==this);
    }
    public static void main(String[] args) throws Exception {
        //创建一个自定义的线程对象
        Demo3 d = new Demo3("狗娃");
        d.setPriority(1);
        d.start(); 
        System.out.println("自定义线程的优先级:"+ d.getPriority());      
//      Thread.sleep(1000);  指定线程睡眠的毫秒数
        Thread mainThread = Thread.currentThread(); // 返回当前线程.
        System.out.println("主线程的优先级:"+ mainThread.getPriority());   //默认的优先级是5 .
        mainThread.setPriority(10);  //设置线程的优先级  优先级越高的线程得到cpu的概率越大。    优先级的范围:1~10 
        System.out.println("主线程的名字:"+ mainThread.getName());
        for(int i = 0 ; i<100 ; i++){           System.out.println(mainThread.getName()+":"+i);
        }   
    }
}
 需求: 模拟车站卖50张票。

问题1:50张票被卖了150次 ?

原因:num是非静态的成员变量,非静态的成员变量每创建一个SaleTickets对象的时候,都会在内部维护一份num数据,这时候创建了三个SaleTickets对象,所以就有三份num数据。

解决方案: 使用static修饰票数num,让该数据共享出来给所有的对象使用。

问题2: 出现了线程安全问题。

线程安全问题出现的根本原因:
1. 存在着两个或者两个以上的线程。
2. 多个线程共享了着一个资源,而且操作资源的代码有多句。
线程安全问题的解决方案:

1. 使用同步代码块

    格式:
        synchronized(锁对象){
            需要被同步的代码;
        }       

同步代码块要注意的细节:
1. 锁对象可以是任意的对象、.
2. 锁对象必须是多个线程共享的对象(锁对象必须是唯一)。
3. 线程调用了sleep方法是不会释放锁对象的。
4. 只有会出现线程安全问题的时候才使用java的同步机制(同步代码块和同步 函数),,

练习: 模拟两夫妻去银行取钱, 一个拿着卡,一个拿着存折 , 每次取100块, 总额是5000块。 不准出现线程安全问题。

2. 同步函数
class SaleTickets extends Thread{
    static  int num = 50;  //非静态成员变量。 非静态成员变量在每个对象中都维护了一份数据。
    static  Object o = new Object();
    public SaleTickets(String name){
        super(name); //调用父类一个参数的构造函数, 初始化线程的名字。
    }
    //线程的任务代码...
    @Override
    public void run() {
        while(true){
            synchronized ("锁") {
                    if(num>0){
                        try {
                        Thread.sleep(100);                  System.out.println(Thread.currentThread().getName()+"卖出了"+num+"号票");
                    num--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                        }
                    }else{
                System.out.println("售罄了...");
                        break;
                    }
            }   
        }
    }
}
public class Demo4 {
    public static void main(String[] args) {
        //创建线程对象
    SaleTickets thread1 = new SaleTickets("窗口1");
    SaleTickets thread2 = new SaleTickets("窗口2");
    SaleTickets thread3 = new SaleTickets("窗口3");
        //开启线程
        thread1.start();
        thread2.start();
        thread3.start();            
    }
}

多线程好处:
1. 解决了在一个进程中 可以同时执行多个任务代码的问题。
2. 提高资源的利用率

多线程的弊端:
1. 增加了cpu的负担
2. 降低了一个进程中线程的执行概率。
3. 引发了线程安全问题。
4. 引发了死锁现象。

自定义线程的步骤:
1. 自定义一个类继承Thread
2. 重写run方法,把自定义线程的任务定义在run方法中。
3. 创建Thread子类的对象,然后调用start方法开启线程。

注意: 千万不要直接调用run方法,直接调用run相当于调用了一个普通的方法而已,并没有开启一个新的线程。

线程的生命周期状态图
sleep\wait()
<————-临时阻塞<————-
调用了start方法 抢夺了cpu的执行权 执行完任务
创建 ———————> 可运行 —————————–>运行 ————–>消亡
<—————————
被抢夺了cpu的执行权

线程安全问题出现的根本原因:
1. 必须存在两个或者两个以上的线程。
2. 多个线程共享着一个资源,而且操作资源的代码有多句。

出现了线程安全问题的解决:

1. 同步代码块

    格式:
        synchronized(锁对象){
            需要被同步的代码
        }

同步代码块要注意的事项:
    1. 锁对象可以是任意的一个对象。
    2. 锁对象必须是多个线程共享 的资源。
    3. 调用了sleep方法的线程并不会释放锁对象。
    4. 如果不存在着线程安全问题,千万不要使用同步代码块或者是同步函数, 因为会降低效率的。


2. 同步函数  : 使用synchronized修饰该函数则称作为同步函数。

同步函数要注意的事项:
    1. 非静态同步函数的锁对象是this对象,静态函数的锁对象是当前所属类的class文件对象。 
    2. 同步函数的锁对象是固定的,无法更改。


推荐使用: 同步代码块

推荐的原因:
    1. 同步代码块的锁对象可以由我们自己指定,同步函数的锁对象是固定 的。
    2. 同步代码块可以随意指定那个范围需要被同步,而同步函数必须是整个函数都同步, 代码不灵活。

练习: 夫妻俩一起去银行取钱, 一个拿着存折, 一个拿着卡,同时取钱。 每次取100块,账户的总额是5000块,不准出现 线程安全问题。


class BankThread extends Thread{
    static  int count = 5000;   //账户的总额
    public BankThread(String name){
        super(name);
    }
    //非静态静态的同步函数 -----  锁对象this对象。
    @Override
    public  void run() {  
        getMoney();
    }
    //静态的同步函数 ---------->当前方法所属的类的class文件对象。
    public synchronized static void getMoney(){
        while(true){
            if(count>0){            System.out.println(Thread.currentThread().getName()+"取走了100块,还剩余"+(count-100)+"块");
                count-=100;
            }else{
                System.out.println("取光了...");
                break;
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //创建线程对象
        BankThread thread1 = new BankThread("老公");
        BankThread thread2 = new BankThread("老婆");
        //调用start方法启动线程。
        thread1.start();
        thread2.start();    
    }
}

自定义线程的创建方式:

方式一: 
    1. 自定义一个类继承Thread.
    2. 子类重写run方法,把自定义线程的任务定义在run方法上。
    3. 创建thread子类的对象,并且调用start方法开启线程。

方式二:
    1. 自定义一个类去实现Runnable接口。
    2. 实现了Runnable接口的run方法, 把自定义线程的任务定义在run方法上。
    3. 创建Runnable实现类的对象。
    4. 创建Thread对象,并且把Runnable实现类对象作为参数传递进去。
    5. 调用thread对象的start方法开启线程。

疑问1: Runnable实现类对象是线程对象吗?
runnable实现类的对象并不是一个线程对象,只不过是实现了Runnable接口的对象而已。

疑问2: 为什么要把Runnable实现类的对象作为参数传递给thread对象呢?作用是什么?
作用: 是把Runnable实现类的对象的run方法作为了任务代码去执行了。

推荐使用: 推荐使用第二种。 因为java是单继承的。

public class Demo3 implements Runnable{
    @Override
    public void run() {
        for(int i = 0 ; i< 100 ; i++){      System.out.println(Thread.currentThread().getName()+":"+i);
        }   
        System.out.println("当前线程对象:"+Thread.currentThread());  // 当前线程对象是: Thread  
        System.out.println("当前对象:"+ this);   //this对象: Demo3的对象
    }
    public static void main(String[] args) {
        //创建Runnable实现类的对象
        Demo3 d = new Demo3();
        //创建Thread对象,并且把Runnable实现类对象作为参数传递进去
        Thread t = new Thread(d,"狗娃");      
        //调用thead对象的start方法开启线程。
        t.start();                  
        /*
            1. Thread类使用了target变量记录了Runnable实现类对象。      
             public void run() {  //run方法的代码是属于线程的任务代码
                if (target != null) {  
                    target.run();    // 调用了Runanble实现类对象的run方法。 就是相当于把Runnable实现类的run方法作为了线程的任务代码去执行了。
                }
             }                   
         */                 
        //主线程执行的。
        for(int i = 0 ; i< 100 ; i++){
    System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}这里写代码片

/*
需求:使用线程的第二种创建方式实现买票的例子
*/

class SaleTickets implements Runnable{  
     int num = 50;  //非静态成员变量
    @Override
    public  void run() {  //this

        while(true){    
            synchronized ("锁") {
                if(num>0){
                    System.out.println(Thread.currentThread().getName()+"卖出了第"+ num+"号票");
                    num--;
                }else{
                    System.out.println("售罄了...");
                    break;
                }           
        }
    }       
    }   
}
public class Demo4 {
    public static void main(String[] args) {
        //创建Runnable实现类的对象
        SaleTickets saleTickets = new SaleTickets();
        //创建三个线程对象
        Thread t1 = new Thread(saleTickets,"窗口1");
        Thread t2 = new Thread(saleTickets,"窗口2");
        Thread t3 = new Thread(saleTickets,"窗口3");
        //调用start方法开启线程
        t1.start();
        t2.start();
        t3.start();
    }   
}这里写代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值