多线程概念

并行和并发

 1.并行:多个指令多核CPU上同时执行
 2.并发:同一个CPU核心,交替执行多条指令

进程和线程

 1.进程:正在执行的程序
 2.线程:线程是进程中的一个执行单元(路径),如果一个进程中包含多个线程,这个程序就是多线程程序。
         QQ、迅雷、360、飞秋等

创建线程的方式

线程的实现方式1

Java的API提供了一个Thread类表示线程,创建一个Thread及其子类对象,即是线程对象。

 1.写一个Thread的子类
 2.复写run方法,写线程的执行代码
 3.创建子类对象
 4.调用start()方法,开启线程
 //1.写一个Thread的子类
 public class DownloadThread extends Thread{
     //2.复写run方法. run方法中的代码就是线程的执行代码
     @Override
     public void run() {
         for (int i = 0; i <=100; i++) {
             //获取线程的名称
             String name = getName();
             System.out.println(name+"....正在下载..."+i+"%");
         }
     } 
 }
 public class Demo1 {
     public static void main(String[] args) {
         //创建线程对象
         DownloadThread dt=new DownloadThread();
         //开启线程
         dt.start();
 ​
         //创建线程对象
         DownloadThread dt1=new DownloadThread();
         //开启线程
         dt1.start();
     }
 }

线程的实现方式2

 1.写一个Runnable接口的子类
 2.复写run方法,编写线程的执行代码
 3.创建Thread对象,把Runnable接口的子类对象作为参数传递
 4.调用start()方法,开启线程
 //线程的执行任务
 public class DownloadRunnable implements Runnable{
     //run方法中的代码就是线程的执行代码
     @Override
     public void run() {
         for (int i = 0; i <=100; i++) {
             //获取当前正在执行的线程
             Thread thread = Thread.currentThread();
             //再通过线程来获取名称
             String name = thread.getName();
             System.out.println(name+"....正在下载..."+i+"%");
         }
     }
 }
 public class Demo2 {
     public static void main(String[] args) {
         //线程任务的对象
         DownloadRunnable dr = new DownloadRunnable();
 ​
         //创建线程对象,指定任务
         Thread t1 = new Thread(dr);
         t1.start();
 ​
         //创建线程对象,指定任务
         Thread t2 = new Thread(dr);
         t2.start();
     }
 }

线程的实现方式3

 1.写一个Callable接口的实现类
 2.复写call方法。有返回值
 3.创建Callable的实现类对象
 4.创建FutureTask对象,把Callable的子类对象作为参数传递
 5.创建Thread对象,把FuTureTask对象作为参数传递
 6.调用start方法,开启线程
 ​
 注意:如果想的到线程的执行结果,在start方法调用之后,调用FuTureTask对象的get方法
 public class MyCallable implements Callable<Integer> {
     @Override
     public Integer call() throws Exception {
         //求1~100的和
         int sum=0;
         for (int i = 1; i <= 100; i++) {
             sum+=i;
         }
         return sum;
     }
 }
 public class Demo3 {
     public static void main(String[] args) throws ExecutionException, InterruptedException {
         //3.创建Callable的子类对象
         MyCallable mc=new MyCallable();
         //4.创建FutureTask对象,把Callable的子类对象作为参数传递
         FutureTask<Integer> ft=new FutureTask<>(mc);
 ​
         //5.创建Thread对象,把FuTureTask对象作为参数传递
         Thread t=new Thread(ft);
         //6.调用start方法,开启线程
         t.start();
         //注意:如果想的到线程的执行结果,在start方法调用之后,调用FuTureTask对象的get方法
         Integer result = ft.get();
         System.out.println(result); 
     }
 }

三种实现方式的对比

 1.继承Thread方式:继承Thread类之后不能再继承其他类
 2.实现Runnable接口:扩展性较强,可以继承其他类,同时还可以实现多个接口
 3.实现Callable接口:线程执行完之后有返回结果

Thread类的方法

 public void setName(String name)
     设置线程的名称
 public String getName()
     获取线程的名称
     
 public static Thread currentThread()
     获取当前正在执行的线程对象
 public static void sleep(long time)
     让线程休眠指定的毫秒值。
 ​
 public void setPriority(int newPriority) 
      更改线程的优先级。优先级越高,执行到的几率越大
      范围:[1,10]
 public int getPriority() 
      获取线程的优先级。 
 public void setDaemon(boolean flag)
      设置线程为守护线程
      当主线程停止运行,守护线程也会跟着停止运行。

线程安全问题

多个线程在访问共享数据时,由于CPU的随机性,一个线程还没有执行完,执行权被其他线程抢走了,这个时候就有可能出现线程安全问题。

同步代码块

 //锁对象:可以是任意对象,但是要保证唯一
 synchronized(锁对象){
     //多个线程访问共享数据的代码
 }

以下案例模拟多个线程共卖100张票,为了保证线程安全加上了同步代码块。

 public class TicketRunnable implements Runnable {
     int ticket = 100;
     //Object obj=new Object(); 
     @Override
     public void run() {
         while (true) {
             //锁对象:可以是任意对象,要保证是唯一的
             synchronized (TicketRunnable.class) {
                 if (ticket > 0) {
                     try {
                         Thread.sleep(10);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     //获取线程名称
                     String name = Thread.currentThread().getName();
                     System.out.println(name + "...正在卖第" + ticket + "号票");
                     ticket--;
                 } else {
                     break; 
                 }
             }
         }
     }
 }
 public class Demo4 {
     public static void main(String[] args) {
         TicketRunnable tr=new TicketRunnable();
         //创建两个线程,并执行
         new Thread(tr,"窗口A").start();
         new Thread(tr,"窗口B").start();
     }
 }

同步方法

 //同步方法:一个线程执行这个方法,另一个线程就必须等待
 //同步方法的锁对象:this
 public synchronized void method() {
     if (ticket > 0) {
         try {
             Thread.sleep(10);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
 ​
         //获取线程名称
         String name = Thread.currentThread().getName();
         System.out.println(name + "...正在卖第" + ticket + "号票");
         ticket--;
     }
 }

Lock锁

 ReentrantLock  lock = new ReentrantLock(); 
 //上锁
 lock.lock();  //一个线程执行到这里,其他线程就必须在这里等
 ​
 //有可能产生安全问题的代码
 ​
 //解锁
 lock.unlock(); //等这个线程执行完了,其他线程才能进来执行。

改造上面的同步代码块

 public class TicketRunnable implements Runnable {
     int ticket = 100;
     //创建锁对象
     ReentrantLock  lock = new ReentrantLock(); 
     @Override
     public void run() {
         while (true) {
             lock.lock(); //上锁
             try {
                 //锁对象:可以是任意对象,是唯一的
                 if (ticket > 0) {
                     try {
                         Thread.sleep(10);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
 ​
                     //获取线程名称
                     String name = Thread.currentThread().getName();
                     System.out.println(name + "...正在卖第" + ticket + "号票");
                     ticket--;
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 //不管前面有没有异常发生,锁都得关闭。
                 lock.unlock();
             }
         }
     }
 }

生产者消费者案例

自己实现等待和唤醒

由于CPU的随机性,多个线程在执行的时候是在相互抢夺CPU的执行权。为了让多个线程协同工作,可使用等待和唤醒机制。

消费者和生产者案例就是一个很经典的,利用等待和唤醒,让多个线程达到协同工作的效果。满足的是一种供需关系。

 //桌子
 public class Desk {
     //默认生产10个汉堡包
     private int count = 10;
     //当前桌子的状态 true有   false:没有
     private boolean flag;
     //锁对象,final的作用是让锁对象时唯一的,不能更改
     private final Object lock = new Object();
 ​
     public int getCount() {
         return count;
     }
 ​
     public void setCount(int count) {
         this.count = count;
     }
 ​
     public boolean isFlag() {
         return flag;
     }
 ​
     public void setFlag(boolean flag) {
         this.flag = flag;
     }
 ​
     public Object getLock() {
         return lock;
     }
 }
  • 生产者

 //生产者(厨师)
 public class Cooker implements Runnable {
     public Desk desk; //桌子
 ​
     //通过构造方法,给desk变量赋值
     public Cooker(Desk desk) {
         this.desk = desk;
     }
 ​
     @Override
     public void run() {
         while (true) {
             //同步代码块
             synchronized (desk.getLock()) {
                 if (desk.getCount() == 0) {
                     break;
                 } else {
                     //判断桌子上有没有汉堡包
                     if (desk.isFlag()) { //有
                         //如果有,厨师线程就等待
                         try {
                             //wait()方法,释放锁再等待
                             desk.getLock().wait(); 
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                     } else { //没有
                         System.out.println("生产一个汉堡包");
                         desk.setCount(desk.getCount() - 1);
                         //把状态改为true
                         desk.setFlag(true);
                         //把消费者线程唤醒
                         desk.getLock().notifyAll(); 
                     }
                 }
             }
         }
     }
 }
  • 消费者

 //消费者(吃货)
 public class Fooder implements Runnable{
     private Desk desk;
     
     public Fooder(Desk desk) {
         this.desk = desk;
     }
 ​
     @Override
     public void run() {
         while (true){
             synchronized (desk.getLock()){
                 if(desk.getCount()==0){
                     break;
                 }else {
                     //判断有没有汉堡包
                     if(desk.isFlag()){ //有
                         System.out.println("吃货吃了一个包子");
                         desk.setFlag(false);
                         desk.getLock().notifyAll(); //把生产者唤醒
                     }else { //没有
                         try {
                             desk.getLock().wait(); 
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                     }
                 }
             }
         }
     }
 }
  • 测试类

 public class Demo2 {
     public static void main(String[] args) {
         Desk desk=new Desk();
 ​
         //创建两个线程对象
         Cooker cooker = new Cooker(desk); //生产者任务
         Fooder fooder = new Fooder(desk); //消费者任务
 ​
         new Thread(cooker).start();
         new Thread(fooder).start();
     }
 }

使用阻塞队列实现

Java的API给开发者提供了一种集合叫做阻塞队列,如图所示。它是以队列结构来进行存取元素的,类似于生活中的排队。当放满了就不能再放了,当取没了也就不能再取了。

生产者

 //厨师
 public class Cooker implements Runnable{
     //阻塞队列
     private ArrayBlockingQueue<String> list;
     public Cooker(ArrayBlockingQueue<String> list) {
         this.list = list;
     }
 ​
     @Override
     public void run() {
         while (true){
             //不断的往阻塞队列中放
             try {
                 list.put("汉堡包");
                 System.out.println("生产一个汉堡包");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
 }
  • 消费者

 //吃货
 public class Fooder implements Runnable {
     //阻塞队列
     private ArrayBlockingQueue<String> list;
     public Fooder(ArrayBlockingQueue<String> list) {
         this.list = list;
     }
 ​
     @Override
     public void run() {
         while (true) {
             try {
                 String take = list.take();
                 System.out.println("取出一个" + take);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
 }
  • 测试类

 public class Demo3 {
     public static void main(String[] args) {
         //创建阻塞队列
         ArrayBlockingQueue<String> list = new ArrayBlockingQueue<>(1);
 ​
         //创建生产者任务
         Cooker cooker = new Cooker(list);
         //创建消费者任务
         Fooder fooder = new Fooder(list);
 ​
         //创建两个线程
         new Thread(cooker).start();
         new Thread(fooder).start();
     }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值