java多线程与JUC
进程线程
进程:进程是操作系统分配资源的基本单位。在电脑中,一个软件就是一个进程
线程:线程是CPU调度的基本单位,是进程内的执行单元。相当于一个软件中的不同功能
多线程程序的特点:程序可以同时去做多件事,CPU可以在多件事之间切换,把等待的空余时间充分利用起来,提高程序的运行效率
多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程。
比如:软件中的耗时、等待操作、所有的聊天软件、所有的服务器。
并发并行
并发:在一个CPU交替执行
并行:在多个CPU同时执行
电脑CPU线程数就表示同一时间可以同时运行的线程数量
4线程,表示可以同时并行4个线程,当时要运行的线程数量过多时,就会并发和并行同时发生
多线程实现方式
1,继承Thread类
- 先自己定义一个类进程Thread类
- 重写run方法,编写线程要执行的代码
- 创建子类对象,调用start方法,启动线程
2,实现Runnable接口
- 创建一个自己类实现Runnable接口
- 重写run方法,编写线程要执行的代码
- 创建自己类的对象
- 创建一个Thread类的对象并传递自己类的对象,并开启线程
3,实现Callable接口或者Future接口(有返回值)
对前面两种的优化,可以获取到多线程运行的结果。
- 创建一个类MyCallable(自己随便创建的类)实现callable接口
- 重写call方法(是有返回值的,表示多线程运行的结果)
- 以下在测试类中写
- 创建MyCallable的对象(表示多线程要执行的任务)
- 创建FutureTask的对象(将MyCallable对象传入。作用管理多线程运行的结果,Future是一个接口)
- 创建Thread类的对象,并启动(将FutureTask对象传入。表示线程)
三种实现方式对比
Thread中常见的成员方法
细节:
- 如果我们没有给线程设置名字,线程也是有默认的名字(格式:Thread-X,X是序号,从0开始)。创建Thread对象时,也可以利用构造方法设置名字
- 当JVM虚拟机启动之后,会自动的启动多条线程。其中有一条线程就叫做main线程,他的作用就是去调用main方法,并执行里面的代码。
- 哪条线程执行到sleep方法,那么哪条线程就会在这里停留对应的时间。方法的参数:就表示睡眠的时间,单位是毫秒。1 秒=1000毫秒。当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
线程优先级相关方法
线程调度:抢占式调度(随机性,相互抢),非抢占式调度(所有线程轮流执行)
java中是采用了抢占式调度,所以有设置线程优先级的方法。
设置线程的优先级越大,抢到CPU的概率越大,执行的机会就越大。一共分10档(1-10,没有设置默认为5)
守护线程方法
将某个线程设置为守护线程(备胎线程),其他非守护线程(女神线程)执行结束,守护线程陆续结束,而不是立刻结束,守护线程会执行一小会。
出让线程/插入线程(了解即可)
前面线程执行都是随机的,为了让线程执行尽可能雨露均沾就提出了出让/礼让线程
哪个线程执行了yield方法,就表示出让了当前CPU执行权,让另一个线程执行
哪个线程执行了join方法,就表示把该线程插入到当前线程之前,执行完后再执行别的
线程生命周期
注意:一个线程sleep完后,不会立马执行,需要去抢CPU执行权,抢到了才能执行
线程安全问题
在卖票时,在还没打印出来时,就被别的线程抢走,票的数量加1,再打印出现超卖和没有第1张票
利用同步代码块
将操作共享数据的代码锁起来。
特点1:锁默认打开,有一个线程进去了,锁自动关闭
特点2:里面代码全部执行完,线程出来,锁自动打开
锁:锁对象,只要保持唯一就行,加static关键字。
这种方式叫同步代码块
同步代码块的细节:
- synchronized不能写在循坏外面
- synchronized(锁对象),其中锁对象一定要是唯一的,不唯一,那就说明不同线程看的锁是不一样的,那锁相当于没有
public class MyThread extends Thread {//表示这个类所有的对象,都共享ticket数据static int ticket = 0;//0 ~ 99@Overridepublic void run() {while (true) {synchronized (MyThread.class) {//同步代码块if (ticket < 100) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在卖第" + ticket + "张票!!!");} else {break;}}}}}
public class ThreadDemo {public static void main(String[] args) {/*需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票*///创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();//起名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//开启线程t1.start();t2.start();t3.start();}
}
利用同步方法
直接把synchronized关键字加到方法上
不知道将哪些操作写到同步方法里。那就先写同步代码块,再将同步代码块抽取成一个方法即可。
public class MyRunnable implements Runnable {//不用写static,因为MyRunnable对象是作为一个参数,让线程去执行的,所以MyRunnable只会创建一个MyRunnable对象int ticket = 0;@Overridepublic void run() {//1.循环while (true) {//2.同步代码块(同步方法)if (method()) break;}}//thisprivate synchronized boolean method() {//3.判断共享数据是否到了末尾,如果到了末尾if (ticket == 100) {return true;} else {//4.判断共享数据是否到了末尾,如果没有到末尾try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");}return false;}
}
public class ThreadDemo {public static void main(String[] args) {/*需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票利用同步方法完成技巧:同步代码块*/MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}