Java 进程与线程

零、复习昨日

二、进程与线程

一个==进程就是一个应用程序,进程包含线程==
一个进程至少包含一个线程,大部分都是多线程在执行任务(多线程)
==进程是系统资源分配的最小单位==
==线程是进程内部的一个执行任务==
进程内多个线程是共享进程资源,==线程是资源调度的最小单位==
Java程序是多线程:main线程、垃圾回收线程等等

三、创建进程【重要】

创建线程的方式

==继承thread==
==实现Runnable接口==
使用Callable 和 Future 来完成
使用线程池获得线程

3.1 继承Thread

步骤
1、自定义类
2、继承Thread
3、重写 run 方法
4、创建子类对象
5、==调用 start 方法启动线程 【特别强调,开启新线程的方法是 start】== start 方法里面有 run 方法
public class MyThread extends Thread{
    // 重写方法,方法内部,就是该线程执行的任务
    @Override
    public void run() {
        for (int i = 1; i < 11; i++) {
            System.out.println("myThread ---> "+ i);
        }
    }
}
public static void main(String[] args) {
    // 创建一个线程
    MyThread myThread = new MyThread();

    // 启动线程
    myThread.start();

    // main线程执行
    for (int i = 1; i < 11; i++) {
        System.out.println("main ---> " + i);
    }
}
    // 自定义线程和主线程同时执行,并且出现资源城墙情况
使用匿名内部类的形式开启线程
// 匿名内部类开启线程
new Thread(){
    @Override
    public void run() {
        for (int i = 1; i < 11; i++) {
            System.out.println("my-Thread ---> " + i);
        }
    }
}.start();

myThread线程,main线程,my-Thread线程3个在争抢资源,如果没有出现效果,把主线程执行的代码放在最下方,因为主线程代码优先级较高。会优先执行……

3.2 实现Runnable接口

步骤
1、自定义类
2、实现 Runnable 接口
3、重写 run 方法
4、创建子实现类对象
5、把子实现类对象当构造方法的方法参数来创建 Thread 对象
6、由 thread 调用 start 方法开启线程
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 11; i++) {
            System.out.println("Runnable1 --> " + i);
        }
    }
}

public static void main(String[] args) {
        // 4 创建实现类对象
        MyRunnable myRunnable = new MyRunnable();

        // 5 把对相当构造方法参数,创建Thread对象
        Thread thread = new Thread(myRunnable);
        // 6 开启线程
        thread.start();

        // main线程执行
        for (int i = 1; i < 11; i++) {
            System.out.println("main --> " + i);
        }
}
使用匿名内部类的形式创建线程
// 匿名内部类实现 Runnable 接口
new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 1; i < 11; i++) {
            System.out.println("Ruannable2 --> " + i);
        }
    }
}).start();

3.3 区别

继承 Thread 开启线程和实现 Runnable 接口开启线程有什么区别?

一个继承;一个实现
继承 Thread 后,直接创建对象即可调用 start 开启线程
实现 Runnable 接口的子类,还需要再创建 Thread 类对象才会开始调用 start 开启线程
继承 Thread 类就限制了该类在继承别的类,因为类只能单继承;而实现接口的同时可以继承别的类,并且允许多继承。
所以推荐使用接口来实现

四、线程的API

==void start() 开启线程==,会自动run方法执行线程任务
==void run() 执行线程的方法==
run() 方法是start开启线程后,JVM自动调用
void setName(String name) 给线程设置名字
也可以通过构造方法,在创建线程时指定线程名
String getName() 获得线程的名字
==static Thread currentThread()== 返回当前正在执行的线程对象
setPriority(int priority) 设置优先级
级别是1-10 ,默认是5
getPriority() 获得优先级
join() 加入线程,等待该线程终止
join(int milles) 加入线程,最大等待直到毫秒数
void setDaemon(boolean on) 设置守护线程
==static== void yield() 礼让线程,暂停当前线程,让其他线程执行
==static void sleep(long milles) 线程休眠==
会让当前线程暂停执行,==让出系统资源==,让其他线程执行
时间到,当前会继续和其他争抢资源执行
void stop() 结束当前线程,线程死亡(已过式)
被废弃的原因是因为这个中断线程太直接太暴力...
package com.qfFirst.qf0302.haveClass;

import com.qfFirst.qf0216.T.T;

/**
 * @escription:
 * @author:等,一起
 * @data:
 */

public class TestThread3 {
    public static void main(String[] args) {
//        testName();

//        testPriority();

//        testDaemon();

//        testYield();

//        testJoin();

//        testSleep();

        testStop();
    }

    public static void testName(){
        // 通过构造方法设置线程名
        Thread thread = new Thread("和谐号"){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        Thread thread2 = new Thread(){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };
        // 默认线程名,从Thread-0开始
//        System.out.println(thread.getName());
//        System.out.println(thread2.getName());

        // 通过setName设置线程名
        thread2.setName("绿皮");

        thread.start();
        thread2.start();
    }

    public static void testPriority(){
        // 优先级
        // 通过构造方法设置线程名
        Thread thread = new Thread("和谐号"){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        Thread thread2 = new Thread("复兴号"){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        // 默认优先级 5
        System.out.println(thread.getName()+"-->"+ thread.getPriority());
        System.out.println(thread2.getName()+"-->"+ thread2.getPriority());

        // 设置优先级,最高10,最低1,不能越界
        // 注意不是优先级高的先执行,只是大概率上会是
        thread.setPriority(1);
        thread2.setPriority(10);

        thread.start();
        thread2.start();

        // 获得主线程名字,优先级 5
        System.out.println(thread.getName());
    }

    public static void testDaemon(){
        // 守护线程
        // 通过构造方法设置线程名
        Thread thread = new Thread("将军"){
            @Override
            public void run() {
                for (int i = 1; i < 2; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        Thread thread2 = new Thread("士兵"){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        // 开启前设置守护线程
        thread2.setDaemon(true);
        // 一旦设置一个线程为守护线程,其余线程就是被守护的
        thread.start();
        thread2.start();
    }

    public static void testYield(){
        // 礼让线程
        // 通过构造方法设置线程名
        Thread thread = new Thread("红方"){
            @Override
            public void run() {
                for (int i = 1; i < 110; i++) {
                    // 获得当前正在执行线程对象
                    if (i == 50){
                        Thread.yield();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        Thread thread2 = new Thread("蓝方"){
            @Override
            public void run() {
                for (int i = 1; i < 110; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        thread.start();
        thread2.start();
    }

    public static void testJoin(){
        // 加入线程
        // 通过构造方法设置线程名
        Thread thread = new Thread("红方"){
            @Override
            public void run() {
                for (int i = 1; i < 110; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        Thread thread2 = new Thread("蓝方"){
            @Override
            public void run() {
                for (int i = 1; i < 110; i++) {
                    // 获得当前正在执行线程对象
                    if (i == 50){
                        try {
                            thread.join();
                            //线程 蓝方 执行到50,让 红方 加入执行,直到结束,蓝方在执行

                            // 蓝方 到50,让红方加入线程执行指定毫秒值,到时间再继续争抢
//                            thread.join(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };

        thread.start();
        thread2.start();
    }

    public static void testSleep(){
        // 睡眠效果
        // 通过构造方法设置线程名
        Thread thread = new Thread("红方"){
            @Override
            public void run() {
                for (int i = 5; i > 0; i--) {
                    // 线程休眠1秒钟
                    System.out.println(i);
                    try{
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("发射!");
            }
        };

//        Thread thread2 = new Thread("蓝方"){
//            @Override
//            public void run() {
//                for (int i = 1; i < 110; i++) {
//                    // 获得当前正在执行线程对象
//                    System.out.println(Thread.currentThread().getName()+"-->"+i);
//                }
//            }
//        };

        thread.start();
//        thread2.start();
    }

    public static void testStop() {
        // 线程中断
        // 通过构造方法设置线程名
        Thread thread = new Thread("红方") {
            @Override
            public void run() {
                for (int i = 1; i < 110; i++) {
                    // 线程休眠1秒钟
                    if (i == 50){
                        Thread.currentThread().stop();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        };
        thread.start();
    }
}

五、线程状态[面试]

线程的几种状态:
创建/新建/初始
new完线程对象
就绪
调用start
等待/阻塞
join()或者sleep()
运行
执行run()方法
死完/销毁/种植
run方法执行完,或者调用stop()或者 interrupt()中断等

清楚状态之间的转换

六、线程安全【重点】

6.1 线程安全

临界资源:共享资源(同一个对象),一次只可以有一个线程操作,才可以保证准确性
原子操作:不可拆分的步骤,被视作一个整体。其步骤不能被忽视

线程不安全:1)完整的步骤可能会被破坏 ;2)线程内的数据可能被别的线程修改

6.2 线程安全方式

同步方法
给方法加锁,及设置同步关键词==synchronized==
锁是当前对象,即this
同步代码块
将需要安全的代码使用同步代码块包裹,设置锁对象
==锁可以是任意对象==
但是线程之间应该是==同一把锁==才能锁住,

其实就是给需要“同步”,需要“安全”,需要“步骤一直,不能打乱”的代码==加锁==

需求:要求打印机类的两个方法分别被两个线程同时执行。但是方法执行时不能被破坏,即方法执行是不能被另外一个线程抢走资源,要保证方法执行是步骤完整性

// 同一把锁(对象)
private Object obj = new Object();
// 打印机1号
public synchronized void printer1(){
    synchronized (obj){
        System.out.print(1 + " ");
        System.out.print(2 + " ");
        System.out.print(3 + " ");
        System.out.print(4 + " ");
        System.out.println();
    }
}

// 打印机2号
public synchronized void printer2(){
    synchronized (obj){
        System.out.print("一");
        System.out.print("二");
        System.out.print("三");
        System.out.print("四");
        System.out.println();
    }
}
public static void main(String[] args) {
        Printer printer = new Printer();

        new Thread(){
            @Override
            public void run() {
                while (true){
                    printer.printer1();
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                while (true){
                    printer.printer2();
                }
            }
        }.start();
    }
}

6.3 转账案例

需求:假设银行专户类,属性有卡号、余额、名字;有ATM机器。机器内部能接收帐户,给ATM指定取的钱数。多台

// 账户
public class Account {

    private String id;
    private double balance;

    public Account() {
    }

    public Account(String id, double balance) {
        this.id = id;
        this.balance = balance;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
public class ATM extends Thread {

    private Account account;
    private double money;
    // 取钱是多线程操作
    @Override
    public void run() {
        synchronized (ATM.class){
            if (account.getBalance() >= money) {
                // 模拟出钱的时间,目的是让出资源,让另外的线程执行,模拟争抢
                // 加锁后,sleep"抱着锁睡觉",是说线程休眠不会让出锁,即不会让出资源,其他线程无法执行,
                // 等着这个线程休眠结束并且执行完毕
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace( );
                }
                account.setBalance(account.getBalance() - money);
                System.out.println(Thread.currentThread().getName() + "取钱成功,取出" +money );
                System.out.println("余额:" + account.getBalance() );

            } else {
                System.out.println("你有多少钱,心里没数?!" );
            }
        }
    }

    public ATM() {
    }

    public ATM(Account account, double money,String name) {
        super(name);
        this.account = account;
        this.money = money;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
public class Bank {

    public static void main(String[] args) {
        Account account = new Account("888888", 1000);
        new ATM(account,600,"小红").start();
        new ATM(account,600,"小黑").start();
    }
}
变式: 转账案例使用Runable接口来实现
练习

售票: 有一个窗口类售票,总共100张票,窗口不止一个可以多个窗口同时售票. 要保证票不能卖超,不能卖重复的票

6.4 线程安全的方式

不只有synchronized同步方法可以保证线程安全,其实还有很多
ThreadLocal
Volatile
Lock
ReentrantLock

单词:

thread:线程;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值