java语言之线程

tech2022-07-11  165

java语言之线程

一、进程和线程

(1)进程:程序是静止的,只有真正运行时的程序,才被称为进程。

(2)线程:又称轻量级进程。

程序中的一个顺序控制流程,同时也是CPU的基本调度单位。 进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。

二、创建线程

创建线程的2种方式:

继承Thread类。实现Runnable接口。

(1)继承Thread类

步骤:

1)自定义线程类继承Thread类,重写 run() 方法;

2)在需要开启线程的地方,实例化自定义线程类;

3)通过对象调用 start() 开启线程。

案例演示:

public class Demo02 { public static void main(String[] args) { //实例化自定义线程类 MyThread myThread = new MyThread(); //调用 start() 开启线程 //子线程和主线程会交替执行 myThread.start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程:" + i); } } } //自定义线程类继承Thread类,重写 run() 方法 class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("子线程:" + i); } } }

(2)实现Runnable接口

步骤:

1)自定义任务类实现Runnable接口,重写 run() 方法;

2)在需要开启线程的地方,实例化任务类;

3)实例化线程类的同时传递任务类的对象;

4)通过线程类的对象调用 start() 开启线程。

public class Demo02 { public static void main(String[] args) { //实例化任务类 MyThread myThread = new MyThread(); //实例化线程类的同时传递任务类的对象 Thread thread = new Thread(myThread); //通过线程类的对象调用 start() 开启线程 //子线程和主线程会交替执行 thread.start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程:" + i); } } } //自定义任务类实现Runnable接口,重写 run() 方法 class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("子线程:" + i); } } }

(3)课堂案例:10个窗口共同销售1000张票,票销售完毕则提示已售罄

public class Main { public static void main(String[] args) { //循环创建10个对象 for (int i = 1; i <= 10; i++) { new Thread(new Ticketsales("窗口" + i)).start(); } } } class Ticketsales implements Runnable{ //临界资源:多个线程共享一个资源 static int num = 1; String name; public Ticketsales(String name){ this.name = name; } @Override public void run() { while (true) { if (num <= 1000) { System.out.println(name + ":卖出第" + num++ + "张票"); } else { System.out.println(name + ":1000张票卖完了!"); break; } } } }

三、线程安全

(1)同步锁:synchronized

注:使用 synchronize 容易出现死锁

1)同步代码块

语法: synchronized( 临界资源对象 ){ // 对临界资源对象加锁 // 代码( 原子操作 ) }

执行过程: 在同一时间使用同步锁锁住的代码块,当监视器为同一对象时,每次只允许一条线程进入同步代码块访问资源,其他相同监视器对象的线程则无法进入,只有等待执行同步代码块的线程释放资源,退出同步代码块后,其他线程才可以获取cpu的资源。

public class Main { public static void main(String[] args) { //循环创建10个对象 for (int i = 1; i <= 10; i++) { new Thread(new Ticketsales("窗口" + i)).start(); } } } class Ticketsales implements Runnable{ //临界资源:多个线程共享一个资源 static int num = 1; String name; public Ticketsales(String name){ this.name = name; } @Override public void run() { while (true) { try { //添加休眠,使程序运行不会太快 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //同步代码块 synchronized ("") { if (num <= 1000) { System.out.println(name + ":卖出第" + num++ + "张票"); } else { System.out.println(name + ":1000张票卖完了!"); break; } } } } }

2)同步方法

语法: synchronized 返回值类型 方法名称( 形参列表 ){ // 对当前对象(this)加锁 // 代码(原子操作) }

执行过程: 被 synchronized 修饰的方法为同步方法,不需要手动添加监视器监视器为方法本身,使用同步方法必须方法为同一个或者是对象为同一个使用同步锁的时候,如果同步锁里调用了 sleep() ,线程的资源不会被释放如果调用的是 wait() 使线程进入等待(阻塞状态),线程资源会被释放。

public class Main { public static void main(String[] args) { //循环创建10个对象 for (int i = 1; i <= 10; i++) { new Thread(new Ticketsales("窗口" + i)).start(); } } } class Ticketsales implements Runnable{ //临界资源:多个线程共享一个资源 static int num = 1; String name; public Ticketsales(String name){ this.name = name; } @Override public void run() { Ticket(); } //同步方法 public synchronized static void Ticket(){ while (true) { try { //添加休眠,使程序运行不会太快 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (num <= 1000) { System.out.println(name + ":卖出第" + num++ + "张票"); } else { System.out.println(name + ":1000张票卖完了!"); break; } } } }

(2)互斥锁:Lock

Lock 是一个接口,需要显式地获取锁和释放锁,不容易出现死锁

public class Main { public static void main(String[] args) { //循环创建10个对象 for (int i = 1; i <= 10; i++) { new Thread(new Ticketsales("窗口" + i)).start(); } } } class Ticketsales implements Runnable{ //临界资源:多个线程共享一个资源 static int num = 1; String name; //多态创建ReentrantLock的对象 Lock lock = new ReentrantLock(); public Ticketsales(String name){ this.name = name; } @Override public void run() { while (true) { try { //添加休眠,使程序运行不会太快 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //获取锁 lock.lock(); if (num <= 1000) { System.out.println(name + ":卖出第" + num++ + "张票"); } else { System.out.println(name + ":1000张票卖完了!"); break; } //释放锁 lock.unlock(); } } }

四、守护线程

概念: 当一条线程在其他线程中创建并设置为守护线程,被守护的线程一旦结束,守护线程跟着销毁,垃圾回收机制也是一个守护线程。

public class Main { public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); //设置子线程为主线程的守护线程,当主线程结束时,子线程也跟着结束 //setDaemon()这个方法的作用是将该线程标记为守护线程或用户线程。 thread.setDaemon(true); thread.start(); Thread.sleep(1); for (int i = 0; i <= 100; i++) { System.out.println("主线程:"+i); } } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i <= 1000; i++) { System.out.println("守护线程:"+i); } } }

五、线程池

什么是线程池: 就是事先创建若干个可执行的线程放入一个池中,需要的时候就从池中获取线程不用自行创建,使用完毕不用销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

线程池的作用: 使用线程池管理线程可以最大程度的利用线程,减少线程的创建和销毁,从而节省资源消耗,它通过利用已有的线程多次循环执行多个任务从而提高系统的处理能力。

创建线程池的步骤: 1、创建线程池对象; 2、创建任务类实现Runnable接口; 3、通过线程池对象提交任务类对象; 4、关闭线程池。

(1)newSingleThreadExecutor,创建一个包含单条线程的线程池

public class Main { public static void main(String[] args) { //创建一个包含单条线程的线程池 ExecutorService pool = Executors.newSingleThreadExecutor(); //创建任务类 Task task = new Task(); for (int i = 0; i < 10; i++) { //submit()方法是提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 pool.submit(task); } //关闭线程池 pool.shutdown(); } } class Task implements Runnable{ @Override public void run() { try { //休眠5毫秒 Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }

(2)创建一个可复用并固定数量线程的线程池

public class Main { public static void main(String[] args) { //创建一个可复用并固定数量线程的线程池 ExecutorService pool = Executors.newFixedThreadPool(3); //创建任务类 Task task = new Task(); for (int i = 0; i < 10; i++) { //submit()方法是提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 pool.submit(task); } //关闭线程池 pool.shutdown(); } } class Task implements Runnable{ @Override public void run() { try { //休眠5毫秒 Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }

(3)创建一个线程数量可变的线程池

public class Main { public static void main(String[] args) { //创建一个线程数量可变的线程池 ExecutorService pool = Executors.newCachedThreadPool(); Task task = new Task(); for (int i = 0; i < 10; i++) { pool.submit(task); } pool.shutdown(); } } class Task implements Runnable{ @Override public void run() { try { //休眠5毫秒 Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }

五、线程的合并

概念:运行中的线程遇到其他线程调用join(),会使当前线程进入等待状态,等待调用join()的线程运行结束,等待中的线程会结束等待状态。

//2条线程,线程A打印1-100,线程B打印1-200,线程B打印到50的时候进入等待状态,等待线程A打印完成后再继续打印 public class Demo01 { public static void main(String[] args) { ThreadA threadA = new ThreadA(); threadA.start(); new ThreadB(threadA).start(); } } class ThreadA extends Thread{ @Override public void run() { int i = 1; while (i<=100){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程A:"+i++); } } } class ThreadB extends Thread{ Thread thread; public ThreadB(Thread thread){ this.thread = thread; } @Override public void run() { int i = 1; while (i<=200){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //当线程B打印到50的时候进入等待状态,等待线程A打印完成后再继续打印 if(i == 50){ try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程B:"+i++); } } }

六、线程的中断

概念:在线程中是没有可供调用的方法来结束线程的,只能改变线程的中断状态,然后在run方法中对中断状态进行处理;当线程处于阻塞状态时,无法改变线程的中断状态。

//2条线程,线程A打印1-100,线程B打印1-200,线程A打印到50的时候改变B线程的中断状态,线程B判断中断状态是否为true,是则结束run() public class Demo01 { public static void main(String[] args) { ThreadB threadB = new ThreadB(); threadB.start(); new ThreadA(threadB).start(); } } class ThreadA extends Thread{ private Thread thread; public ThreadA(Thread thread){ this.thread = thread; } @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("线程A:"+i); if(i == 50){ //改变B线程的中断状态 thread.interrupt(); } } } } class ThreadB extends Thread{ @Override public void run() { for (int i = 1; i <= 200; i++) { //判断中断状态是否为true,是则结束run() if(isInterrupted()){ return; } System.out.println("线程B:"+i); } } }

七、线程的优先级

概念:使用setPriority()方法设置线程的优先级别,但不是绝对优先,设置后不影响CPU的调度。

public class Demo01 { public static void main(String[] args) { MyThread thread1 = new MyThread("线程A"); MyThread thread2 = new MyThread("线程B"); //把先启动的线程设置为最低级别 thread1.setPriority(Thread.MIN_PRIORITY); //把后启动的线程设置为最高级别 thread2.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); } } class MyThread extends Thread{ public MyThread(String name){ super.setName(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(super.getName()+":"+i); } } }

八、线程的礼让

概念:使用yield()方法后退出运行状态,回到就绪状态,与其他线程一起等待 CPU 的调度。

public class Demo01 { public static void main(String[] args) { new MyThread1().start(); new MyThread2().start(); } } class MyThread1 extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if(i >=100){ //线程A打印到100的时候进行礼让,让线程B先运行 Thread.yield(); } System.out.println("线程A:"+i); } } } class MyThread2 extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程B:"+i); } } }

九、线程安全的集合

注:绿色代表新增知识,下划线代表线程安全集合。

案例演示:CopyOnWriteArrayList

线程安全的ArrayList,加强版读写分离。 写有锁,读无锁,读写之间不阻塞,优于读写锁。 写入时,先copy一个容器副本、再添加新元素,最后替换引用。 使用方式与ArrayList无异。

public class Demo01 { public static void main(String[] args) { //读写冲突 //List<String> list = new ArrayList<>(); List<String> list = new CopyOnWriteArrayList<>(); list.add("a"); list.add("b"); list.add("c"); for (String str:list) { list.add("1"); System.out.println(str); } System.out.println("--------------"); for (String str:list) { System.out.println(str); } } } // 运行结果: // a // b // c // -------------- // a // b // c // 1 // 1 // 1
最新回复(0)