• 多线程


    一、实现线程的两种方式:

    1)继承Thread类

    完成线程的的代码放在run()中,对其进行覆盖;

    public ThreadTest extends Thread(){
    
      
    public void run(){
    
    //线程代码
    
    }
     } 

    //启动线程 new ThreadTest().start();

    2)实现Runnable接口

    实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例

    class MyRunnable implements Runnable {
         private int i = 0;
          @Override
         public void run() {
              for (i = 0; i < 100; i++) {
                  System.out.println(Thread.currentThread().getName() + " " + i);
             }
         }
     }
    public class ThreadTest {
      
         public static void main(String[] args) {
             for (int i = 0; i < 100; i++) {
                 System.out.println(Thread.currentThread().getName() + " " + i);
                 if (i == 30) {
                     Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
                     Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
                    Thread thread2 = new Thread(myRunnable);
                    thread1.start(); // 调用start()方法使得线程进入就绪状态
                    thread2.start();
                 }
             }
         }
    }

    两种方法创建线程时注意区别

    二、线程的生命周期

    7种状态:出生--就绪---执行---等待、阻塞、休眠、死亡

    休眠:Thread.sleep(2000);//休眠2s

    线程的加入:threadB.join();

    join方法使当前线程暂停,直到join的线程执行完毕

    线程中断:stop()方法

    线程的优先级

    三、线程的同步

    为了线程安全,防止线程冲突

    java提供了线程同步的机制来防止资源访问的冲突

    线程同步机制:

    同一时间只允许一个线程访问,object可以为任意对象

    1)同步块

    synchronized(Object){

      //共享的资源,如代码块

    }

    2)同步方法

    synchronized void  f(){

    }

     ------------------------------------------------------------------------

    10.6号更新

    • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
    • 并行与并发:
      • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
      • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
    并发与并行
    • 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
      void transferMoney(User from, User to, float amount){
        to.setMoney(to.getBalance() + amount);
        from.setMoney(from.getBalance() - amount);
      }
    • 同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

    好了,让我们开始吧。我准备分成几部分来总结涉及到多线程的内容:

    1. 扎好马步:线程的状态
    2. 内功心法:每个对象都有的方法(机制)
    3. 太祖长拳:基本线程类
    4. 九阴真经:高级多线程控制类

    扎好马步:线程的状态

    先来两张图:


    线程状态

    线程状态转换


    各种状态一目了然,值得一提的是"blocked"这个状态:
    线程在Running的过程中可能会遇到阻塞(Blocked)情况

    1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
    2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
    3. 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。

    此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

    内功心法:每个对象都有的方法(机制)

    synchronized, wait, notify 是任何对象都具有的同步工具。让我们先来了解他们

    monitor


    他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。

    wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。

    当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。

    再讲用法:

    • synchronized单独使用:
      • 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
      • 复制代码
        public class Thread1 implements Runnable {
           Object lock;
           public void run() {  
               synchronized(lock){//代码块
                 ..do something
               }
           }
        }
        复制代码
      • 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
        public class Thread1 implements Runnable {
           public synchronized void run() {  //方法
                ..do something
           }
        }
    • synchronized, wait, notify结合:典型场景生产者消费者问题

      复制代码
      /**
         * 生产者生产出来的产品交给店员
         */
        public synchronized void produce()
        {
            if(this.product >= MAX_PRODUCT)
            {
                try
                {
                    wait();  
                    System.out.println("产品已满,请稍候再生产");
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
                return;
            }
      
            this.product++;
            System.out.println("生产者生产第" + this.product + "个产品.");
            notifyAll();   //通知等待区的消费者可以取出产品了
        }
      
        /**
         * 消费者从店员取产品
         */
        public synchronized void consume()
        {
            if(this.product <= MIN_PRODUCT)
            {
                try 
                {
                    wait(); 
                    System.out.println("缺货,稍候再取");
                } 
                catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
                return;
            }
      
            System.out.println("消费者取走了第" + this.product + "个产品.");
            this.product--;
            notifyAll();   //通知等待去的生产者可以生产产品了
        }
      复制代码

    调用了wait()

    wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法,

    调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。

    多线程有几种实现方法?同步有几种实现方法?

    多线程有两种实现方法,分别是继承Thread类与实现Runnable接口

    同步的实现方面有两种,分别是synchronized,waitnotify

    wait():使一个线程处于等待状态,并且释放所持有的对象的lock

    sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

    notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

    Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

    启动一个线程是用run()还是start()? .

    启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

    当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

    分几种情况:

         1.其他方法前是否加了synchronized关键字,如果没加,则能。

         2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。

         3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能

         4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this 

    线程的基本概念、线程的基本状态以及状态之间的关系

     状态:就绪,运行,synchronize阻塞,waitsleep挂起,结束。wait必须在synchronized内部调用。

      调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。

      • 复制代码
        public class Thread1 implements Runnable {
           Object lock;
           public void run() {  
               synchronized(lock){
                 ..do something
               }
           }
        }
        复制代码
      • 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
        public class Thread1 implements Runnable {
           public synchronized void run() {  
                ..do something
           }
        }
    • synchronized, wait, notify结合:典型场景生产者消费者问题

      复制代码
      /**
         * 生产者生产出来的产品交给店员
         */
        public synchronized void produce()
        {
            if(this.product >= MAX_PRODUCT)
            {
                try
                {
                    wait();  
                    System.out.println("产品已满,请稍候再生产");
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
                return;
            }
      
            this.product++;
            System.out.println("生产者生产第" + this.product + "个产品.");
            notifyAll();   //通知等待区的消费者可以取出产品了
        }
      
        /**
         * 消费者从店员取产品
         */
        public synchronized void consume()
        {
            if(this.product <= MIN_PRODUCT)
            {
                try 
                {
                    wait(); 
                    System.out.println("缺货,稍候再取");
                } 
                catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
                return;
            }
      
            System.out.println("消费者取走了第" + this.product + "个产品.");
            this.product--;
            notifyAll();   //通知等待去的生产者可以生产产品了
        }
      复制代码
  • 相关阅读:
    ArrayList用法
    Delegate比较全面的例子(原创)
    一个登陆页面,包含了初始化用户,输入检测,错误处理等
    C#线程 在某一时间内,只有N个线程在并发执行,其余都在队列中的实现
    ASP.Net防止刷新自动触发事件的解决方案
    存储过程编写经验和优化措施 (转)
    [转]数据库开发21条军规
    [转]Ajax简单客户登陆验证
    用SqlBulkCopy进行大批量数据迁移
    什么时候使用哪个数据绑定控件(asp.net)
  • 原文地址:https://www.cnblogs.com/limingxian537423/p/7524317.html
Copyright © 2020-2023  润新知