java多线程入门 -------- 收割常见并发编程的基础知识

一.线程与进程

现代操作系统调度的最小单位是线程,也叫轻量级进程,在一个进程里可以创建多个线程,线程是程序程序内部的一个执行路径,而一个进程可以有多个执行路径。线程都拥有各自的计数器,堆,栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。

二.并行和并发

并行:指两个或两个以上的活动在同一时刻发生。
并发:指在某一时刻只有一个事件或活动发生,某个时间段内由于CPU的交替执行,可以发生多个时间。

三.开启多线程的方法

1.继承Thread类,重写run方法

class MyThread extends Thread{
     @Override
     public void run(){
        System.out.println("hello Thread !");
     }
}

public class Main{
	public static void main(String[] args){
	 	MyThread thread = new MyThread();
	 	thread.start();    //调用start()方法开启多线程,会自动调用run()方法
	}
}

注意事项:
到这里大家可能会发现一个问题,为什么我们不直接去调用对象的run方法,而是要通过start方法去间接调用呢?
需要注意的是,当我调用start方法时,他不仅会帮我们去调用run方法,更重要的是会去创建一个新的线程,然后该线程去调用run方法。而直接去调用run方法,则不会创建一个新的线程。

2.实现Runable接口,重写run方法

class runimpl implements Runable{
    @OverRide
    public void run(){
       System.out.println("hello runable");
    }
}

//主线程
public class Main{
	public static void main(String[] args){
	    runimpl r = new runimpl();
	    Thread t = new Thread(r);  //这个地方可以使用Lambda表达式简写 new Thread(()->{Ststem.out.println("hello runnable")},"新线程");
	    t.start();
	}
}

四.为什么要使用多线程?

1.更多的处理核心
线程是大多数操作系统调度的基本单元,一个程序作为一个进程来运行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理核心上。试想一下一个单线程程序在运行时只能使用一个处理核心,那么再多的处理核心加入也无法显著提高该程序的执行效率。相反,如果该程序使用多线程技术,再将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多的处理核心的加入而变得更有效率。
2.更快的响应时间
有时我们会编写一些较为复杂的代码,例如一笔订单的创建,它包括插入订单数据,生成订单快照,发送邮件通知卖家和记录货品的销售数量。用户从点击“订购”按钮开始,就要等待这些操作全部完成才能看到订购成功的效果。但是这么多业务操作,如何能够让其更快的完成呢?可以使用多线程技术,即将数据一致性不强的操作派发给其他线程处理,如生成订单快照,发送邮件等。这样做的好处是相应用户请求的线程能够尽快的处理完成,缩短了响应时间。
3.更好的编程模型
java多线程编程提供了良好,考究并且一致的编程模型,使开发人员更加专注于问题的解决,即为遇到的问题建立合适的模型,而不是绞尽脑汁的考虑如何将其多线程化。

五.线程的生命周期

1.NEW 初始状态,线程被构建,但但还是没有调用start()方法
2.RUNNABLE 运行状态,java线程将操作系统中的就绪和运行两种状态笼统的称为"运行中"
3.BLOCKED 阻塞状态,表示线程阻塞于锁
4.WAITING 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定的"动作"
5.TIME_WAITING 超时等待状态,该状态不同于WAITING,它是可以在指定的时间特定返回的
6.TERMINATED 终止状态,表示当前线程已经执行完毕
java多线程入门 -------- 收割常见并发编程的基础知识_第1张图片

六.线程中常用的方法

1.getName/setName(String) 获取和设置当前线程对象的名字
2.currentThread 获取当前线程的对象,静态方法
3.getPriority/setPriority(int) 设置或获取线程的优先级,java线程的优先级有10个,为1 - 10
4.join 等待该线程终止
5.yield 暂停当前正在执行的线程对象,并执行其他线程。静态方法。
6.sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。静态方法

public class Main{
	public static void main(String[] args){
	   //结构lambda表达式创建一个线程t,线程的名字为A
	   Thread t = new Thread(()->{System.out.println(Thread.currentThread().getName());},"A");     
	    //输出主线程的优先级
	   System.out.println(Thread.currentThread().getPriority());
	   //主线程休眠1秒
	   Thread.sleep(1000);
	}
}

七.线程安全问题及其解决方案

当多个线程去争夺同一个资源的时候就会产生线程安全问题,为了避免这样的问题的发生。我们就引入的线程的同步机制,对于一些共享的资源,在某个时刻只能有一个线程去访问。那么如何才能保证某一时刻只能有一个线程去访问共享资源呢?使用synchronized关键字是解决该问题的方法之一。
为了更加理解我们的多线程安全问题,在这里我们结合经典的"买票问题"进行分析。

/*
问题分析:
票的资源类
卖票的线程(假设多个)
同步方法解决线程安全问题
*/

class Ticket{
    private int number = 100;  //假设初始有100张票
    
    //卖票的的方法,必须要加synchronized关键字
    public synchronized void saleTicket(){
        //只有当票的数量大于0的时候才可以买票
    	if(number>0){
    	   System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--));
    	}
   }
}


/*
//同步代码块解决多线程安全问题
class Ticket{
   private int number = 100;
   private static Object obj = new Object();

  //卖票的方法,同步代码块解决此问题,注意锁对象必须是同一个,所以在这里我们将obj声明为静态对象
  public void saleTicket(){
     synchronize(obj){
        if(number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--));
        }
     }
  }
}

*/

public class Main{
	public static void main(String[] args){
	   Ticket ticket = new Ticket();    //票的资源类
	   new Thread(()->{for(int i=0;i<=101;i++){ticket.saleTicket();}},"A").start();  //卖票线程A
	   new Thread(()->{for(int i=0;i<=101;i++){ticket.saleTicket();}},"B").start();  //卖票线程B
	   new Thread(()->{for(int i=0;i<=101;i++){ticket.saleTicket();}},"C").start();  //卖票线程C
	}
}

多线程安全问题产生原因的分析:
当没有对共享变量进行访问控制时,就会出现多个进程对同一个资源的争夺,这便会产生线程安全问题
java多线程入门 -------- 收割常见并发编程的基础知识_第2张图片

当对共享变量进行了访问控制后
java多线程入门 -------- 收割常见并发编程的基础知识_第3张图片

注意事项:
1.普通同步方法的锁对象是this,而静态同步方法的锁对象是当前类的Class对象。
2.那些操作会释放锁对象,那些操作不会释放锁对象?
java多线程入门 -------- 收割常见并发编程的基础知识_第4张图片

你可能感兴趣的:(java并发编程的艺术,java,多线程,编程语言)