java高级编程——多线程

    xiaoxiao2025-01-20  12

    一、最常见的创建线程的两种方式

    1.继承Thread

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/26. */ public class MyThread1 extends Thread { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); MyThread1 t2 = new MyThread1(); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } @Override public void run() { for (int i = 0; i < 10; i++) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+i); } } }

    【详解】继承Thread类,重写父类的run()方法,然后执行一系列操作。程序首先从mai()方法进入,然后通过new  MyThread1()创建了两个对象,调用setName()可以修改线程名,调用start()方法可以让线程被开启。run()方法会被回调,其中currentThread()是静态方法,sleep()方法也是Thread的静态方法,代码里的sleep()其实就是this.sleep(),而this就是该方法被调用的对象,很显然是Thread.sleep();

    2、实现Runnable接口

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/26. */ public class MyThread2 implements Runnable { public static void main(String[] args) { MyThread2 t1 = new MyThread2(); MyThread2 t2 = new MyThread2(); Thread thread1=new Thread(t1); Thread thread2=new Thread(t2); thread1.setName("线程1"); thread2.setName("线程2"); thread1.start(); thread2.start(); } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+i); } } }

    【详解】第二种方式是通过实现Runnable接口,重写run()方法,而我们还是需要newThread(),调用start()方法开启线程。这里的sleep()前面需要加Thread,因为此时的run()是new Thread()这个对象调用的,this指向的是new Thread()这个对象

    【运行结果】

    二、线程的生命周期

    创建了线程之后,调用start()方法,线程进入就绪状态,只有当线程获得了CPU,就会到达运行状态,此时run()方法就会被执行,可能还没执行完,失去了CPU,就会回到就绪状态,等待下次获取到CPU之后继续执行run()方法。这就是为什么上述例子两个子线程交互执行for循环。通过调用sleep()方法可以让线程进入阻塞状态,等待时间到后又回到就绪状态。

    三、给线程加锁

    简单介绍java的内存分配,jvm内存主要包括

    栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。

     堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。

    方法区:包含如下内容:

     常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于方法区中。

    代码段:用来存放从硬盘上读取的源程序代码。

    数据段:用来存放static定义的静态成员。

    》》其中每个线程都会有一个属于自己的栈,而堆、方法区都是所有线程共享的,这就存在数据同步的问题。比如在售票的时候,多个窗口去共享同一个数据,这个时候就会出现重票或负票的情况。线程在共享数据方面通过加锁来实现线程同步,就比如很多人排队上厕所,然后一个一个的进去,进去一个锁一下门,等他出来后把锁交给另一个人。

    实现线程同步的方法有两种:

    1.同步代码块

    2.同步方法

    1.实现Runnable的同步代码块

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/24. */ public class ThreadTest implements Runnable{ /** * 线程同步: * 1.同步代码块synchronized(同步监视器:锁){ * //需要同步的代码 * } * 2.同步方法 * */ //任何对象都能充当锁 Object obj=new Object(); public int ticket=100; @Override public void run() { while (true){ synchronized (obj) {//synchronized (this) { if(ticket>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票"); ticket--; }else { break; } } } } public static void main(String[] args) { ThreadTest t1=new ThreadTest(); new Thread(t1).start(); new Thread(t1).start(); new Thread(t1).start(); } }

    2.实现Runnable的同步代码块

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/24. */ public class ThreadTest2 implements Runnable{ /** * 线程同步: * 1.同步代码块synchronized(同步监视器:锁){ * //需要同步的代码 * } * 2.同步方法 * */ public int ticket=100; @Override public void run() { while (true){ Boolean b=reduce(); if(b){ break; } } } public synchronized Boolean reduce(){ if(ticket>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票"); ticket--; }else { return true; } return false; } public static void main(String[] args) { ThreadTest2 t1=new ThreadTest2(); new Thread(t1).start(); new Thread(t1).start(); new Thread(t1).start(); } }

    3.继承Thread的同步代码块

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/24. */ public class ThreadTest3 extends Thread { public static int ticket = 100; public static void main(String[] args) { ThreadTest3 t1 = new ThreadTest3(); ThreadTest3 t2 = new ThreadTest3(); ThreadTest3 t3 = new ThreadTest3(); t1.start(); t2.start(); t3.start(); } @Override public void run() { while (true) { synchronized (ThreadTest3.class) {//synchronized (obj) { if (ticket > 0) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票"); ticket--; } else { break; } } } } }

    4.继承Thread的同步代码块

    package com.xioayu.threadTest1; /** * Created by hasee on 2019/5/24. */ public class ThreadTest4 extends Thread { public static int ticket = 100; public static void main(String[] args) { ThreadTest4 t1 = new ThreadTest4(); ThreadTest4 t2 = new ThreadTest4(); ThreadTest4 t3 = new ThreadTest4(); t1.start(); t2.start(); t3.start(); } @Override public void run() { while (true) { Boolean b=ThreadTest4.reduce(); if(b){ break; } } } public static synchronized boolean reduce() { if (ticket > 0) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票"); ticket--; }else { return true; } return false; } }

    四、让线程堵塞和唤醒线程

    package com.xioayu.threadTest2; /** * Created by hasee on 2019/5/24. */ public class Number implements Runnable { public int ticket=100; @Override public void run() { while (true){ synchronized (this) { notifyAll(); if(ticket>0) { System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票"); ticket--; try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } public static void main(String[] args) { Number t1=new Number(); new Thread(t1).start(); new Thread(t1).start(); } }

    【结果】

    wait():让线程进入堵塞状态,并释放锁,只要不唤醒,一直在堵塞状态。Object类中申明,只能在同步方法块内调用

    sleep() :让线程进入堵塞状态,不释放锁,时间结束后自动进入就绪状态,就是其他线程抢占了CPU也得不到锁,只有等该线程再次得到CPU执行结束或者释放锁后其他线程开可能得到锁。Thread类中申明,任何场景都可调用

    notify()和notifyAll():唤醒线程,Object类中申明

    生成者消费者例子

    package com.xioayu.threadTest2; /** * Created by hasee on 2019/5/25. */ public class ThreadTest5 { public static void main(String[] args) { Apple apple=new Apple(); Consumer c1=new Consumer(apple); Producer p1=new Producer(apple); Producer p2=new Producer(apple); Thread t1=new Thread(c1); Thread t2=new Thread(p1); Thread t3=new Thread(p2); t1.setName("生成者1"); t2.setName("消费者1"); t3.setName("消费者2"); t1.start(); t2.start(); t3.start(); } } class Consumer implements Runnable{ private Apple apple; Consumer(Apple apple){ this.apple=apple; } @Override public void run() { while (true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } apple.reduce(); } } } class Producer implements Runnable{ private Apple apple; Producer(Apple apple){ this.apple=apple; } @Override public void run() { while (true){ try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } apple.add(); } } } class Apple{ private int number=0; public synchronized void add(){ if(number<5){ number++; System.out.println("已供应第"+number+"个苹果"); notifyAll(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void reduce(){ if(number>0){ System.out.println("已消费第"+number+"个苹果"); number--; notifyAll(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }

    五、未来任务

    这是JDK5.0之后新增的一种创建线程的方式,让原本返回值为void的线程有了返回值,便于线程之间的通信

    package com.xiaoyu.threadTest3; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * Created by hasee on 2019/5/25. */ public class FutureTaskTest { public static void main(String[] args) { MyCallable myCallable = new MyCallable(); FutureTask<List<User>> listFutureTask = new FutureTask<>(myCallable); Thread thread = new Thread(listFutureTask); thread.setName("子线程"); thread.start(); try { List<User> list=listFutureTask.get(); for(User user:list){ System.out.println(user.getUsername()+"---"+user.getPassword()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } class MyCallable implements Callable<List<User>>{ @Override public List<User> call() throws Exception { List<User> list=new ArrayList<User>(); for (int i=0;i<10;i++) { Thread.sleep(100); User user = new User(); user.setUsername(i+""); user.setPassword(i+""); list.add(user); } return list; } } class User{ private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

    结果

    六、线程池

    package com.xioayu.threadTest2; import java.util.concurrent.*; /** * Created by hasee on 2019/5/25. */ public class ThreadPoolTest { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); /* ThreadPoolExecutor service1= (ThreadPoolExecutor) service; service1.setCorePoolSize(5); service1.setKeepAliveTime(); service1.setMaximumPoolSize(15);*/ service.execute(new MyThread()); Future<Integer> submit = service.submit(new MyThread2()); try { Integer integer = submit.get(); System.out.println("返回值"+integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { for (int i=0;i<100;i=i+2){ System.out.println(Thread.currentThread().getName()+":"+i); } } } class MyThread2 implements Callable<Integer>{ @Override public Integer call() throws Exception { for (int i=1;i<100;i=i+2){ System.out.println(Thread.currentThread().getName()+":"+i); } return 3; } }

    execute:实现Runnable接口

    submit:实现Callable接口

    最新回复(0)