zoukankan      html  css  js  c++  java
  • Java-多线程

    Java多线程编程

    Java虚拟机允许应用程序并发地运行多个执行线程,它们会交替运行,彼此之间可以进行通信。

    每个线程都有一个优先级,这样有助于系统确定线程的调度顺序。

    多线程并发执行可以提高程序效率,更加充分的使用CPU。

    并行和并发

    • 并行:就是同时执行,比如你边看电视边吃薯片。(需要多核CPU来实现)

    • 并发:就是交替执行,比如你看会电视,然后吃会薯片,再看会电视,再吃会薯片......

    ​ 指的是两个任务都在请求运行,但是处理器只能执行一个任务,就把这两个任务安排轮流执行,

    ​ 由于时间间隔较短,使人感觉两个任务都在运行。

    1.1 创建新线程的两种方式:

    1.继承Thread 类重写run() 方法;

    class MyThread extends Thread{
        @Override
        public void run() {
            // To do anything you want.
        }
    }
    

    调用:

      MyThread thread = new MyThread();
      thread.start();
    

    2.实现Runnable接口重写run()方法;

     class MyRunnable implements Runnable{
            @Override
            public void run() {
                // To do anything you want. 
            }
        }
    

    调用:

         MyRunnable runnable=new MyRunnable();
         new Thread(runnable).start();
    

    注意最后都是通过 Start() 方法来开启线程。

    1.2 两种方式区别:

    继承Thread:

    好处:可以直接使用Thread中方法,代码简洁。

    弊端:如果已经有了父类,则不能使用这种方式。

    实现Runnable接口:

    好处:当线程类中已经有了父类时可以使用。

    弊端:不能直接使用Thread中的方法,需要先获取到线程对象,才能使用,代码复杂。

    使用时需优先考虑Thread

    2. Thread 常用方法:

    方法名 描述
    currentThread() 返回当前执行的线程对象
    getName() 返回该线程的名称
    setName(String name) 设置线程名称
    sleep(long millis) 使线程休眠(暂停执行),参数为毫秒
    start() 使线程开始执行;Java虚拟机调用该线程的run()方法
    setPriority(int priority) 更改线程优先级
    setDaemon(bolean on) 将该线程标记为守护线程或用户线程
    isDaemon() 判断线程是为守护线程
    isAlive() 判断当前线程是否为活动状态
    join() 让主线程等待子线程结束后再继续运行(会阻塞主线程)
    yield() 暂停当前正在执行的线程对象,并执行其他线程

    3.守护线程

    Java线程中有两种线程: User Thread(用户线程), Daemon Thread(守护线程,也叫后台线程)

    新创建的线程默认都是前台线程,只有当创建线程为守护线程时,新线程才是守护线程。

    当所有用户线程都退出后,守护线程也会退出。(举个例子来说的话就是:当你关闭浏览器后,所有的网页都会关闭)

    可以通过 setDaemon(true) 改变线程为守护线程,注意该方法必须在start()方法之前调用。

    4.线程的优先级

    每个Java线程都有有一个优先级,默认优先级是NORM_PRIORITY(5)。

    优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程。

    设置优先级有助于操作系统确定线程的调度顺序,但是不能保证线程执行的顺序,也就是说高优先级的线程不一定先执行完。

    当某个线程中创建一个新的Thread对象,该新线程的初始优先级被设定为创建线程的优先级。

    JDK预定义优先值为:

        public final static int MIN_PRIORITY = 1;  //The minimum priority that a thread can have.
        public final static int NORM_PRIORITY = 5; //The default priority that is assigned to a thread.
        public final static int MAX_PRIORITY = 10; //The maximum priority that a thread can have.
    

    设置优先级使用setPriority()方法,该方法源码为:

     public final void setPriority(int newPriority) {
            ThreadGroup g;
            checkAccess();
            if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
                throw new IllegalArgumentException();
            }
            if((g = getThreadGroup()) != null) {
                if (newPriority > g.getMaxPriority()) {
                    newPriority = g.getMaxPriority();
                }
                synchronized(this) {
                    this.priority = newPriority;
                    if (isAlive()) {
                        nativeSetPriority(newPriority);
                    }
                }
            }
        }
    

    根据源码我们可以得出,优先级取值范围为1~10,如果小于1或者大于10则将抛出异常。

    5.线程同步

    多线程开发时,当有多段代码需要执行,我们希望某一段代码执行的过程中CPU不要切换到其他线程

    这就需要用到同步代码块,这样的话只有当同步代码块执行结束后才会执行其它代码。

    通过关键字 synchronized 来实现 ,格式:

      //任意对象都可以当做锁对象
      //注意:匿名对象不能当做锁对象,因为不能保证两个锁对象是同一个对象
      synchronized(lock){    //获取锁
       //需要同步的代码块;
      }  //释放锁
    

    除了代码块可以同步外,方法也是可以同步的。

    声明格式 :

      synchronized void 方法名称(){}
    

    同步方法虽然没有声明锁对象,但是它也是有锁对象的:

    非静态同步方法:this 静态同步方法:类名.class (就是当前类的字节码对象)

    锁对象,它是同步代码块的关键。当线程执行同步代码块时,首先会检查锁对象的标志位,默认标志位为1,此时线程会执行同步代码块,同时将锁对象标志位置为0。当一个新的线程执行到这段代码块是,由于锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后,锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码。循环往复,知道共享资源被处理完位为止。这个过程就好比一个公用电话亭,只有前一个人都打完电话出来后,后面的人才可以打。

    注意:同步代码块中锁对象可以使任意类型的对象,但是多个线程共享的锁对象必须是唯一的。

    6.死锁

    死锁就是两个或多个线程被永久阻塞是的一种情形。

    假设这样一场景,小明去买冰棍:

    售货员:“你先给我钱,我再给你冰棍”

    小明:“你先给我冰棍,我再给你钱”

    最终的结果就是小明买不到冰棍,售货员也得不到钱。

    在这里钱跟冰棍就是锁,两个线程在运行时都在等待对方的锁,这样就造成的阻塞,这种现象称为死锁。

    接下来通过代码来模拟:

    
    class DeadLockThread implements Runnable {
    
    	static Object ice = new Object(); // 锁对象:冰棍
    	static Object money = new Object(); // 锁对象:钱
    
    	private boolean flag; // true:小明 false:售货员
    
    	public DeadLockThread(boolean flag) {
    		this.flag = flag;
    	}
    
    	@Override
    	public void run() {
    		if (flag) {  //小明
    			while (true) {
    				synchronized (money) {
    					    System.out.println("小明: 给我冰棍");
    					synchronized (ice) {
    						System.out.println("售货员: 给你冰棍");
    					}
    				}
    			}
    
    		} else {  //售货员
    			while (true) {
    				synchronized (ice) {
    					    System.out.println("售货员: 给我钱");
    					synchronized (money) {
    						System.out.println("小明: 给你钱");
    					}
    				}
    			}
    		}
    	}
    }
    
    

    创建两个线程:

    		DeadLockThread t1=new DeadLockThread(true); //小明
    		DeadLockThread t2=new DeadLockThread(false); //售货员
    		
    		//创建并开启两个线程
    		new Thread(t1).start();
    		new Thread(t2).start();
    

    输出结果:

    小明: 给我冰棍
    售货员: 给我钱
    

    最终小明锁着”钱”,售货员锁着”冰棍”,小明得不到冰棍,售货员得不到钱。

    在开发中我们是尽量死锁的,方式之一就是尽量不要使用同步代码块的嵌套。

    7.单例设计模式

    目的:保证类在内存当中只有一个对象。

    两种写法:

    //饿汉式
    class Singleton{
       //1.私有构造函数
       private Singleton() {}
       //2.创建本类对象
       private static Singleton s=new Singleton();
       //3.对外提供公共的访问方法
       public static Singleton getInstance(){
         return s;
       }
     }
    
     //懒汉式
     class Singleton{
        //1.私有构造函数
       private Singleton() {}
       //2.创建本类对象
       private static Singleton s;
        //3.对外提供公共的访问方法
       public static Singleton getInstance(){
         if(s==null)
           s=new Singleton();
         return s;
       }
     }
    
  • 相关阅读:
    ApplicationContext
    消息队列种类
    Zookeeper的功能以及工作原理
    java多线程(1)模式 不可变对象
    dubbo容错,负载均衡,协议
    dubbo的底层原理
    消息事务处理
    多线程循环打印 abc
    Spring 事务管理
    文件上传和下载
  • 原文地址:https://www.cnblogs.com/-Tiger/p/8658505.html
Copyright © 2011-2022 走看看