千峰教育2218期线程

今天的内容

进程
线程【重点】
今天学习的目标是要入门线程。知道线程是干嘛的!!!!!

1. 进程

是独立的一个应用程序
比如咱们电脑上面,一个软件就是一个应用程序,就是一个进程,idea、 QQ
window 系统会给进程分配当前电脑中资源,可以分享当前的网络,网卡,内存,显卡等
进程有些特性:
独立性
各个进程之间是相互独立的互不影响!!!
互斥性
每个软件 windows 系统会给他们分配唯一的端口号,如果一个软件启动了电脑会给分配一个独立的端口号,如果其他软件启动,电脑再给分配另一个端口号,端口号是唯一的。
比如 QQ 端口号是 97 idea 端口号也是 97. 意味着 QQ 启动之后 idea 无法启动,会报错,端口被占用的错误

2. 线程

进程是可以运行的,在运行的时候,线程是进程中最小的运行单位。每一个进程至少有一个线程
如果一个进程没有线程,那么这个进程就没有生命力。
进程包含了线程,线程是组成进程的最小的基本单位
线程特性:
抢占式运行【很重要今天能理解线程】
一个进程在执行的时候,靠获取 CPU 时间片来执行的。单位时间片是抢占式执行的。
比如一个进程里面有是三个线程在执行,同时执行的 ? 不是 三个线程先抢占
假如线程1先抢到以后,线程 1 先执行 4ms, 然后是释放资源。三个线程再抢 线程 2 再执行。执行 4ms
如此往复。抢占间隔的时间是比较短的!!所以你感觉这个应用程序一致在执行的!!!
资源共享性
线程之间可以共享网卡和 CPU 的
Java 程序:
比如咱们写过的 Demo1 main 主函数 就是一个进程,就是一个应用程序。
Demo1 这个 Java 应用程序中有几个线程?
两个:
main 主线程
JVM 垃圾回收线程

3. 并行和并发

并行:真正意义上的同时执行,我一边做饭一边看电视
并发:同时发生轮流交替执行,我看一会电视做一会饭

4. 创建线程的两种方式【重点】

一个线程干一个活,比如咱们可以让一个线程去打印 99 乘法变,让另一个线程打印等腰直角三角形!!!

创建线程的第一种方式:
创建一个新的执行线程有两种方法。一个是将一个类声明为 Thread 的子类,这个子类应该重写 Run 类的方法 Thread 。然后可以分配并启动子类的实例。
package com.study.a_thread;

/**
 * @author big God
 * @date 2023/1/2 10:23 * @version 1.0
 */
// 1.自己新建一个类声明为 Thread 子类
class MyThread1 extends Thread {
    // 2. 重写 run 方法
    @Override
    public void run() {
        // MyThread1 线程执行的任务
        for (int i = 0; i < 500; i++) {
            System.err.println("MyThread1 线程" + i);
        }
    }
}
class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("MyThread2 线程" + i);
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        // 3. 实例化线程
        MyThread1 myThread1 = new MyThread1();
        // 4. 启动线程  使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
        myThread1.start();
        new MyThread2().start();
    }
}

练习:新建两个线程一个线程打印九九乘法表一个线程打印直角三角形 15分钟能不能写完

package com.study.a_thread;

/**
 * @author big God
 * @date 2023/1/2 10:42 * @version 1.0
 */
class Multiplication extends Thread {
    @Override
    public void run() {
        for (int i = 1; i < 10; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print(i + "*" + j + " = " + i * j + "\t");
            }
            System.out.println();
        }
    }
}
class Triangle extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < i + 1; j++) {
                System.out.print("* ");
            }
            System.out.println();
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        new Multiplication().start();
        new Triangle().start();
        // 线程得到的东西不可预期,所以难!!! 每次执行的结果不一样
    }
}
创建线程的第二种方式
另一种方法来创建一个线程是声明实现类 Runnable 接口。那个类然后实现了 run 方法。然后可以分配类的实例,在创建 Thread 时作为参数传递,并启动。
package com.study.b_thread;

/**
 * @author big God
 * @date 2023/1/2 11:16 * @version 1.0
 */
// 1. 实现 Runnable 接口
class MyThread1 implements Runnable {
    @Override
    // 2. 重写 run 方法
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread1 线程" + i);
        }
    }
}
class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread2 线程" + i);
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //3. 创建 MyThread1 的对象
        //4. 在创建 Thread 对象的时候将线程对象作为参数传递进来
        // Thread(Runnable target)
        //          分配新的 Thread 对象。
        // 5. 开启线程
        new Thread(new MyThread1()).start();
        // 匿名内部类实现 Runnable 接口
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.err.println("匿名内部类实现 Runnable接口 " + i);
                }
            }
        }).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程" + i);
        }
        // 启动 MyThread2 线程
        // 匿名对象的写法
        new Thread(new MyThread2()).start();
    }
}
练习:新建两个线程打印九九乘法表一个线程打印直角三角形!!
使用第二种方式来写!!! 【匿名内部类也可以写】
package com.study.b_thread;

/**
 * @author big God
 * @date 2023/1/2 11:30 * @version 1.0
 */
public class Demo2 {
    public static void main(String[] args) {
        // 匿名内部类实现 Runnable 接口 九九乘法表
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 10; i++) {
                    for (int j = 1; j < i + 1; j++) {
                        System.out.print( i + "*" + j + "=" + i * j + "\t");
                    }
                    System.out.println();
                }
            }
        }).start();
        // 匿名内部类实现 Runnable 接口 直角三角形
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    for (int j = 0; j < i + 1; j++) {
                        System.out.print("* ");
                    }
                    System.out.println();
                }
            }
        }).start();
    }
}

开发中使用第二种方式,因为多个接口

5. 线程下面几个基础方法

构造方法】
线程下面的方法
package com.study.c_thread;

/**
 * @author big God
 * @date 2023/1/2 11:46 * @version 1.0
 */
class MyThread1 implements  Runnable {
    @Override
    public void run() {
        // currentThread() 获取当前线程的引用 当前 MyThread1 对象
        System.out.println("Line7:" + Thread.currentThread());
        // Thread-0 线程默认的名字
        System.out.println("Line8:" + Thread.currentThread().getName());
    }
}
public class Demo1 {
    public static void main(String[] args) {
        // 通过构造方法对线程起名字
        Thread thread = new Thread(new MyThread1(),"线程1");
        System.out.println("Line13:" + thread);
        // 覆盖构造方法所起的名字
        thread.setName("MyThread1 线程");
        thread.start();



        // 匿名内部类实现 Runnable 接口
        // 通过构造方法对线程起名字
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // currentThread() 获取当前线程的引用 当前 MyThread1 对象
                // Thread-1 线程默认的名字
                System.out.println("Line9:" + Thread.currentThread());
                System.out.println("Line3:" + Thread.currentThread().getName());
            }
        }, "匿名内部类线程2");
        thread1.start();
        // 在 main 主函数中写 currentThread() 主线程 获取主线程的名字
        // main 是主线程的默认名字
        //
        Thread.currentThread().setName("主线程");
        System.out.println(Thread.currentThread().getName());
        // 发现咱们的线程 JVM 都会给一个默认的值,咱们呢过不能自己给线程起名字 ? 可以的

    }
}
package com.study.c_thread;

/**
 * @author big God
 * @date 2023/1/2 12:18 * @version 1.0
 */
class Mythread2 implements  Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread2 线程" + i);
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Thread myThread2 = new Thread(new Mythread2(), "MyThread2");
        myThread2.setPriority(10);
        Thread.currentThread().setPriority(1);
        int priority = myThread2.getPriority();
        // 线程优先级 默认是 5, 值可以设置为 1 ~ 10, 1 的优先级是最低的 10 的优先级最高的
        // 优先级并不是真正的优先,优先级越高就增加执行的概率
        System.out.println(priority); // 5
        myThread2.start();

        // main 主线程中优先级  5
        System.out.println(Thread.currentThread().getPriority());
        for (int i = 0; i < 100; i++) {
            System.err.println("主线程" + i);
        }
    }

}
package com.study.c_thread;

/**
 * @author big God
 * @date 2023/1/2 12:30 * @version 1.0
 */
class MyThread3 implements Runnable {
    @Override
    public void run() {
        // 让 Mythread3 睡一会儿 10000 毫秒
        // 1 秒 = 1000 毫秒
        try {
            // 为啥只能 try - catch 不能 throws
            /**
             * run 方法是重写的方法,重写非常严格
             * 在父类中 run 方法是没有 throws 的
             *  public abstract void run() throws Exception;
             */
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.err.println("MyThread3 线程:" + i);
        }
    }
}
public class Demo3 {
    public static void main(String[] args) {
        new Thread(new MyThread3()).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

上午的内容

1. 创建线程的两种方式
	继承 Thread 类,重写 run 方法
	实现 Runnable 接口,重写 run 方法
2. 线程下面的几个方法
	static void sleep(); 让线程睡一会儿
	static Thread currentThread(); 获取当前线程对象的
	setName() 设置线程名字的
	getName() 获取线程名字的

6. 线程的同步和锁

当时用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现安全问题,比如多个线程都在操作同一个数据,都打算修改商品库存,这样就会导致数据不一致的问题。
线程同步的真实意思,其实是"排队":几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
所以我们用同步机制来解决这些问题,加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
三个人(三个线程)同时对共享资源(一个坑位)进行操作(抢占)。导致数据不安全的效果。咋解决?
同步机制,加锁。一个人上厕所,锁住,其他的两个人在外面等待。等待你解决完以后释放掉这个锁以后。其他的线程再进来抢占这个资源。
package com.study.d_thread;

/**
 *卖票
 * 有两个线程卖票 火车票 总共有 192 张票
 * 一个线程 第 192 张票 另外一个线程 第 191 张票
 * @author big God
 * @date 2023/1/2 14:37 * @version 1.0
 */
class SaleTicket implements Runnable {
    // ticket 共享资源 线程会操作这个共享的资源
    private static int ticket = 192;
    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + ticket + "票");
                ticket--;
            } else {
                System.err.println("卖完了");
                break;
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        // 两个线程访问同一个数据
        SaleTicket saleTicket = new SaleTicket();
        new Thread(saleTicket,"线程1").start();
        new Thread(saleTicket,"线程2").start();
    }
}
线程1卖出了第192票
线程1卖出了第191票
线程1卖出了第190票
线程1卖出了第189票
线程1卖出了第188票
线程1卖出了第187票
线程1卖出了第186票
线程1卖出了第185票    问题出现了
线程2卖出了第185票    问题出现了  为啥同时卖出去了?
线程2卖出了第183票
线程2卖出了第182票
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值