java多线程编程详细介绍

it2025-05-29  4

线程

线程简单介绍

程序:程序是指令和数据的有序集合,它是静态的,没有运行的含义。

进程:进程就是执行程序的过程,它是动态的。是系统资源分配器的单位。

线程:进程可以分成多个单元同时运行,这些再被进程分配的多个单元叫做线程。线程是cpu调度的最小单位。

进程控制块:控制进程。里面存放进程的信息。

下面是关系图: 实际上,一个CPU,同一时刻只能执行一个线程。但是CPU运算速度很快,达到纳秒级别,我们在宏观上是多个任务同时执行,即并行。

多任务:同时执行多个任务。例如:边做什么边做什么。

多线程:多个线程同时执行。

线程的创建

三种创建线程的方式:继承Thread类、实现Runnable接口、实现Callable接口。

Thread类

该类实现了Runnable接口。

继承Thread类创建线程

创建步骤:

自定义一个类继承Thread类重写run方法调用start方法,启动线程。 public class MyThread1 extends Thread { @Override public void run() { for (int i = 0; i < 200; i++) { System.out.println("我是线程1--"+ i); } } public static void main(String[] args) { //创建一个线程对象 MyThread1 myThread1 = new MyThread1(); //开启线程 myThread1.start(); //主线程 for (int i = 0; i < 2000; i++) { System.out.println("我是主线程--" + i); } } }

实现Runnable接口创建线程(推荐)

实现Runnable接口重写run方法。执行线程需要丢入runnable接口的实现类,调用start方法 //创建线程方式2:实现Runnable接口,重写run,执行线程需要传入runnable接口实现类,调用start()方法 public class TestThread3 implements Runnable{ @Override public void run() { //run方法线程体 for(int i = 0; i < 200; i++){ System.out.println("线程1正在运行-------"+i); } } public static void main(String[] args){ //创建线程1 TestThread3 myThread = new TestThread3(); //开启线程 new Thread(myThread).start(); for(int i = 0; i < 2000; i++){ System.out.println("我是主线程正在运行----"+i); } } }

模拟龟兔赛跑

//赛跑 public class Race implements Runnable{ //胜利者 private static String winner; @Override public void run() { //跑道为1000步,如果谁先跑1000步谁就赢 for (int i = 0; i <= 1000; i++) { //模拟兔子睡觉,每10步兔子睡一次觉 if(Thread.currentThread().getName().equals("兔子")&& i%10 == 0 ) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag = gameOver(i); if(flag){ break; } //打印谁跑多少步 System.out.println(Thread.currentThread().getName()+"----跑了"+i+"步"); } } //判断胜利者 private boolean gameOver(int steps) { if (winner != null) { return true; } else { if (steps >= 1000) { winner = Thread.currentThread().getName(); System.out.println(winner + "win"); return true; } } return false; } public static void main(String[] args) { Race race = new Race(); //比赛开始 new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }

实现Callable接口创建线程

实现步骤:

实现callable接口,需要返回值类型重写call方法,需要抛出异常创建目标对象创建执行服务提交服务获取服务关闭服务

图片下载案例:

//线程创建方式3, public class TestCallable implements Callable<Boolean> { private String url; private String name; public TestCallable(String url, String name){ this.url = url; this.name = name; } @Override public Boolean call() { WebDownloader1 webDownloader = new WebDownloader1(); webDownloader.downDownload(url,name); System.out.println("下载文件名:"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { //创建目标对象 TestCallable testThread1 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","1.jpg"); TestCallable testThread2 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","2.jpg"); TestCallable testThread3 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","3.jpg"); //创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> result1 = ser.submit(testThread1); Future<Boolean> result2 = ser.submit(testThread2); Future<Boolean> result3 = ser.submit(testThread3); //获取结果 boolean rs1 = result1.get(); boolean rs2 = result2.get(); boolean rs3 = result3.get(); System.out.println(rs1+"-"+rs2+"-"+rs3); //关闭服务 ser.shutdownNow(); } } class WebDownloader1{ //下载方法 public void downDownload(String url, String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("io异常"); } } }

静态代理

静态代理模式:

目标对象和代理对象都要实现同一个接口

代理对象要代理目标对象

好处:代理对象可以做目标对象做不了的东西,而目标对象可以只关注自己的事情

结婚代理案例:

public class StaticProxy { public static void main(String[] args) { //委托人 You you =new You(); //代理人 WeddingCompany weddingCompany = new WeddingCompany(you); //执行代理方法 weddingCompany.happyMarry(); } } //结婚接口 interface Marry{ //结婚 void happyMarry(); } //结婚对象 class You implements Marry{ //某人结婚 @Override public void happyMarry() { System.out.println("某人结婚"); } } //婚庆公司 class WeddingCompany implements Marry{ //委托人,代理对象 private Marry target; public WeddingCompany(Marry target) { this.target = target; } //举办婚礼 @Override public void happyMarry() { System.out.println("布置现场"); this.target.happyMarry(); System.out.println("收拾现场"); } }

线程的五大状态

线程的五大状态:创建状态,就绪状态、运行状态、阻塞状态、死亡状态。

线程停止

建议线程正常停止,利用次数,不建议死循环。简历使用标志位,设置标志位。不建议使用stop,destroy方法。

线程休眠

sleep指定当前线程阻塞的毫秒数sleep存在异常InterruptedExceptionsleep时间到达后线程进入就绪状态sleep可以模拟网络延时,倒计时等。每一个对象都是锁,sleep不会释放锁

休眠案例:

public class TestSleep2 implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("倒计时:"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { //10秒倒计时 new Thread(new TestSleep2()).start(); //获取系统当前时间 Date startTime = new Date(System.currentTimeMillis()); while (true){ try { System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); Thread.sleep(1000); startTime = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } }

线程礼让

礼让方法:yield()

线程礼让,让当前执行的线程暂停,但不阻塞

让cpu重新调度,礼让不一定成功

将线程从运行转为就绪状态

线程强制执行——join

public class TestJoin implements Runnable{ public static void main(String[] args) throws InterruptedException { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 1000; i++) { if(i == 200){ thread.join(); } System.out.println("main"); } } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程插队"); } } }

观察测试线程状态

用getState()获取当前线程的状态。

线程的优先级

Java提供一个线程调度器类监控程序中启动后进入就绪状态的所有线程,线程调度按照优先级决定应该调度哪个线程来执行

线程的优先级用数字表示,范围从1-10

getPriority()和setPriority(int xxx)用来设置和获取优先级

注意:先设置再start。高优先级不一定先调用。

public class TestPriority{ public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+ "->" +Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread thread1 = new Thread(myPriority); Thread thread2 = new Thread(myPriority); Thread thread3 = new Thread(myPriority); Thread thread4 = new Thread(myPriority); Thread thread5 = new Thread(myPriority); Thread thread6 = new Thread(myPriority); thread1.start(); thread2.setPriority(1); thread2.start(); thread3.setPriority(4); thread3.start(); thread4.setPriority(Thread.MAX_PRIORITY); thread4.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+ "->" +Thread.currentThread().getPriority()); } }

守护线程

线程分为用户线程和守护线程虚拟机必须确保用户线程执行完毕虚拟机不用等待守护线程执行完毕 public class TestDaemon { public static void main(String[] args) { God god = new God(); You1 you1 =new You1(); Thread thread1 = new Thread(god); thread1.setDaemon(true);//默认false,代表用户线程 thread1.start(); //上帝线程启动 new Thread(you1).start();//你线程启动 } } //上帝 class God implements Runnable{ @Override public void run() { while(true){ System.out.println("上帝线程守护你线程"); } } } //你 class You1 implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("你线程执行"); } System.out.println("你线程结束"); } }

线程同步(线程安全)

线程同步:出现在多个线程操作同一个资源。是一种等待机制。

线程同步:sychronized关键字。

并发:同一个对象被多个线程同时操作。

并发问题:

//多个线程同时操作同一个对象 //买火车票 public class TestThread4 implements Runnable{ //票数 private int ticketNums = 10; @Override public void run() { while (ticketNums > 0) { try { //模拟延时 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "线程1拿票第" + ticketNums-- + "票"); } } public static void main(String[] args) { TestThread4 ticket = new TestThread4(); new Thread(ticket,"小明").start(); new Thread(ticket,"小花").start(); new Thread(ticket,"小红").start(); } }

执行结果:

小红线程1拿票第10票 小花线程1拿票第9票 小明线程1拿票第10票 小红线程1拿票第8票 小花线程1拿票第8票 小明线程1拿票第7票 小花线程1拿票第6票 小明线程1拿票第4票 小红线程1拿票第5票 小红线程1拿票第3票 小花线程1拿票第1票 小明线程1拿票第2票

sychronized

sychronized使用:

sychronized方法

在方法用sychronized修饰

public sychronized void buy(){ } //sychronized相当于一把锁,它把这个方法对应的对象锁着,一次只能一个人访问该对象。 //对于上述买车票问题线程同步实现 public sychronized void run() { while (ticketNums > 0) { try { //模拟延时 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "线程1拿票第" + ticketNums-- + "票"); } } //该锁锁的是TestThread4这个对象

sychronized块

sychronized(obj){ 实现; } //块锁的是该obj对象,一次只能一个人访问该对象,而不是方法对应的对象。 //将上述买票重新写一下,用锁块实现同步,这个锁的是ticket这个对象 class Ticket{ public int ticketNums = 20; } public class UnsafeBuyTicket implements Runnable{ //票数 private int ticketNums = 10; boolean flag = true; Ticket ticket = new Ticket(); @Override public void run() { //买票 while(flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } public void buy() throws InterruptedException { synchronized (ticket){ if(ticket.ticketNums <= 0){ this.flag = false; return; } Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName() + "买到第" + ticket.ticketNums-- +"张票"); } } public static void main(String[] args) { UnsafeBuyTicket unsafeBuyTicket = new UnsafeBuyTicket(); new Thread(unsafeBuyTicket,"小明").start(); new Thread(unsafeBuyTicket,"小华").start(); new Thread(unsafeBuyTicket,"小红").start(); } }

死锁

死锁:都在相互等待对方的资源。

产生死锁的4个必要条件:

互斥条件:一个支援每次只能被一个进程使用。请求和保持条件:一个进程因请求资源而堵塞时,对方获得的资源褒词不放。不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免程序中出现死锁。

锁(Lock)

Lock是接口,常用实现类ReentrantLock(可重复锁)。

import java.util.concurrent.locks.ReentrantLock; //测试lock锁 public class TestLock { public static void main(String[] args) { TestLock1 testLock11 = new TestLock1(); new Thread(testLock11).start(); new Thread(testLock11).start(); new Thread(testLock11).start(); } } class TestLock1 implements Runnable{ int ticket = 10; ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock(); if(ticket >0){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticket--); }else { break; } }finally { lock.unlock(); } } } }

线程通信

生产者消费者 问题

管程法

//生产者消费者模型,管程法 //生产者,消费者,产品,缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } //生产者 class Productor extends Thread{ SynContainer container; public Productor(SynContainer container){ this.container = container; } ReentrantLock lock = new ReentrantLock(); @Override public void run() { produce(); } void produce() { for (int i = 0; i < 30; i++) { container.push(new Thing(i+1)); System.out.println("生产了第"+ (i+1) +"个产品"); } } } //消费者 class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } @Override public void run() { for (int i = 0; i < 30 ; i++) { System.out.println("消费了第"+ container.pop().id +"个产品"); } } } //产品 class Thing{ int id;//产品编号 public Thing(int id) { this.id = id; } } //缓冲区 class SynContainer{ Thing[] things = new Thing[10]; int count = 0; //生产者放入产品 public synchronized void push(Thing thing){ //容器满,等待消费者取 if(count == things.length){ //等待消费者取 try{ this.wait(); }catch (InterruptedException e){ e.printStackTrace(); } } //容器未满,放产品 things[count++] = thing; this.notifyAll(); } //消费者取出产品 public synchronized Thing pop(){ //判断是否能消费 if(count == 0){ //等待生产者放产品 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Thing thing = things[count]; this.notifyAll(); return thing; } }

线程池

//ExecutorService 线程池接口 //Executors 线程池工具类 //创建线程池,参数位线程池中的线程个数 ExecutorService ser = Executors.newFixedThreadPool(2) //Runnable接口用execute()无返回值 ser.execute(new RunnableImpl) //Callable接口用submit()有返回值 Future<V> result = ser.submit(new CallableImpl);
最新回复(0)