1.线程相关的概念html
1.程序(program):是一个静态的状态,通常存储在硬盘中 2.进程(process):一个正在运行的程序是一个动态的概念,通常存储在内存中。 3.线程(thread):一条独立的执行路径。多线程,在执行某个程序的时候,该程序能够有多个子任务,每一个线程均可以独立的完成其中一个任务。在各个子任务之间,没有什么依赖关系,能够单独执行。
2.进程和线程的关系java
一个进程中至少有一个线程,能够有多个线程,一个进程中的全部线程,共享同一个进程中的资源
3.并行和并发数据库
并行(parallel):多个任务(进程、线程)同时运行。在某个肯定的时刻,有多个任务在执行,条件:有多个cpu,多核编程 并发(concurrent):多个任务(进程、线程)同时发起。不能同时执行的(只有一个cpu),只能是同时要求执行。就只能在某个时间片内,将多个任务都有过执行。一个cpu在不一样的任务之间,来回切换,只不过每一个任务耗费的时间比较短,cpu的切换速度比较快,因此可让用户感受就像多个任务在同时执行。(实际开发中的多线程程序,都是并发运行机制)
4.多线程的实现方式编程
第一种:继承Thread类缓存
1) 定义一个类,继承Thread类 2) 重写自定义类中的run方法,用于定义新线程要运行的内容 3) 建立自定义类型的对象 4) 调用线程启动的方法:start方法
代码实现安全
//自定义类 package com.tohka.pojo; public class MyThread extends Thread { @Override public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } //测试类 package com.tohka.junit; import com.tohka.pojo.MyThread; public class Junit1 { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } //使用匿名内部类的方式来实现 package com.tohka.junit; import com.tohka.pojo.MyThread; public class Junit2 { public static void main(String[] args) { new MyThread(){ @Override public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } }.start(); for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } }
第二种:实现实现Runnable接口多线程
1)定义一个任务类,实现Runnable接口 2)重写任务类中的run方法,用于定义任务的内容 3)建立任务类对象,表示任务 4)建立一个Thread类型的对象,将任务对象做为构造参数传递,用于执行任务类对象 Thread(Runnable able); 5)调用线程对象的start方法,开启新线程 调用的就是Thread类构造方法中传递的able线程任务中的run方法
代码实现:并发
//任务类 package com.tohka.pojo; public class TaskThread implements Runnable { public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } //测试类 package com.tohka.junit; import com.tohka.pojo.TaskThread; public class Junit3 { public static void main(String[] args) { TaskThread ts = new TaskThread(); Thread thread = new Thread(ts); thread.start(); for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } //使用匿名内部类实现 package com.tohka.junit; import com.tohka.pojo.TaskThread; public class Junit3 { public static void main(String[] args) { new Thread(new TaskThread(){ @Override public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } }).start(); for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } }
第三种:实现Callable接口异步
实现思路:ide
若是建立Thread对象,执行Runnable任务,须要Runnable对象
Runnable是一个接口,有一个特殊的实现类
FutureTask是Runnable的一个实现类
FutureTask在建立对象的时候,须要传递一个Callable的对象
Callable是一个接口,因此咱们须要一个Callable的实现类
1)定义一个自定义类实现Callable接口 2)在自定义类中重写call()方法 3)建立自定义类的对象 4)建立Future的实现类FutureTask对象,把自定义类的对象做为构造方法的参数 5)建立Thread类的对象,把FutureTask对象做为构造方法的参数 6)使用Thread类的对象调用start方法启动线程 7)FutureTask对象用get方法,就能够获取线程结束以后的结果。
代码实现
//自定义类 package com.tohka.pojo; import java.util.concurrent.Callable; public class MyCallable implements Callable<Integer> { public Integer call() throws Exception { int i ; for (i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } return i; } } //测试类 package com.tohka.junit; import com.tohka.pojo.MyCallable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Junit4 { public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable callable = new MyCallable(); FutureTask<Integer> futureTask = new FutureTask<Integer>(callable); Thread thread = new Thread(futureTask); thread.start(); Integer i = futureTask.get(); System.out.println(thread.currentThread().getName()+i); } }
第四种:经过线程池实现
1.建立一个指定线程数量的线程池 Executors.newFixedThreadPool(int nThreads) 2.建立任务类对象:Runnable的实现类对象,用于定义任务内容 3.将一个任务类对象,提交到线程池中,若是有空闲的线程,就能够立刻运行这个任务,若是没有空闲线程,那么这个任务就须要等待 submit(Runnable r) 4.结束线程池 shutDown()、shutDownNow()
代码实现
package com.tohka.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo1 { public static void main(String[] args) { ExecutorService ex = Executors.newFixedThreadPool(3); Runnable r1 = new Runnable() { public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } }; Runnable r2 = new Runnable() { public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } }; Runnable r3 = new Runnable() { public void run() { for (int i = 1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } }; ex.submit(r1); ex.submit(r2); ex.submit(r3); ex.shutdown(); } }
5.线程优先级
线程默认优先级是5;线程优先级的范围是:1-10 Thread类提供的优先级常量(静态常量) MIN_PRIORITY = 1; 最低 NORM_PRIORITY = 5; 默认 MAX_PRIORITY = 10; 最高
注意 : 哪怕给线程设定了优先级,没有办法保证,哪些线程必定最早运行; 线程优先级别高的, 获取CPU资源的几率大, 但线程的执行仍然具备很大的随机性
6.线程礼让
static void yield() 线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程若队列中没有同优先级的线程,忽略此方法
测试代码
package com.tohka.junit; public class Junit5 { public static void main(String[] args) { Thread thread1 = new Thread("礼让线程"){ @Override public void run() { System.out.println(getName()); } }; thread1.yield(); thread1.setPriority(Thread.MAX_PRIORITY); Thread thread2 = new Thread("不让线程1"){ @Override public void run() { System.out.println(getName()); } }; thread2.setPriority(Thread.MAX_PRIORITY); Thread thread3 = new Thread("不让线程2"){ @Override public void run() { System.out.println(getName()); } }; thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); } }
执行结果:
不让线程1
礼让线程
不让线程2
出现问题缘由:
没法100%礼让,只能说礼让会尽可能后执行
7.线程中断
中断这个线程休眠,等待状态,只中断一次。
想中断那个线程,必须到另外一个线程中取中断,由于本身不能中断本身
代码案例
package com.tohka.junit; public class Junit5 { public static void main(String[] args) { Thread t3 = new Thread("不让线程2"){ @Override public void run(){ for(int i = 1; i <= 10; i++){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "--" +i); } } }; t3.start(); t3.interrupt(); } }
8.守护线程
若是一个程序的运行中,没有非守护线程了,只有守护线程了,那么守护线程会过一段时间后自动中止
final void setDaemon(boolean on) : 参数设置为true,将线程设置为守护线程
将此线程标记为daemon线程或用户线程。当运行的惟一线程都是守护进程线程时,Java虚拟机将退出
package com.tohka.junit; public class Junit5 { public static void main(String[] args) { Thread thread = new Thread("守护线程") { @Override public void run() { for(int i = 1; true; i++){ System.out.println(getName() + "--" +i); } } }; thread.setDaemon(true); Thread thread1 = new Thread("普通线程") { @Override public void run(){ for(int i = 1; i <= 10; i++){ System.out.println(getName() + "--" +i); } } }; thread.start(); thread1.start(); } }
9.使用同步代码块保证线程安全的原理
当cpu想去执行同步代码块的时候,须要先获取到锁对象,获取以后就能够运行代码块中的内容;当cpu正在执行当前代码块的内容时,cpu能够切换到其余线程,可是不能切换到须要相同锁对象的线程上。 当cpu执行完当前代码块中的代码以后,就会释放锁对象,cpu就能够运行其余须要当前锁对象的同步代码块了
10.同步方法的锁对象
若是是非静态的方法,同步方法的锁对象就是this,当前对象,哪一个对象调用这个同步方法,这个同步方法使用的锁就是哪一个对象 若是是静态的方法,同步方法的锁对象就是当前类的字节码对象,类名.class(在方法区的一个对象),哪一个类在调用这个同步方法,这个同步方法使用的锁就是哪一个类的字节码对象
11.线程的生命周期
新建态:刚建立好对象的时候,刚new出来的时候 就绪态:线程准备好了全部运行的资源,只差cpu来临 运行态:cpu正在执行的线程的状态 阻塞态:线程主动休息、或者缺乏一些运行的资源,即便cpu来临,也没法运行 死亡态:线程运行完成、出现异常、调用方法结束
**12.wait()与 notify()方法 **
wait() 让线程等待 notify()唤醒等待的线程 notify()和 notifyAll() 通常来讲,全部等待的线程 会按照顺序进行排列,若是如今使用了 notify()方法的话,则会唤醒第一个等 待的线程执行,而若是使用了 notifyAll()方法,则会唤醒全部的等待线程,那 个线程的优先级高,那个线程就有可能先执行。
13.sleep() 和 wait()的区别
一、这两个方法来自不一样的类分别是,sleep来自Thread类,wait来自Object类。 二、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其余线程可使用同步控制块或者方法。 sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其余线程能够占用CPU。通常wait不会加时间限制,由于若是wait线程的运行资源不够,再出来也没用,要等待其余线程调用notify/notifyAll唤醒等待池中的全部线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)能够用时间指定使它自动唤醒过来,若是时间不到只能调用interrupt()强行打断。 Thread.Sleep(0)的做用是“触发操做系统马上从新进行一次CPU竞争”。 三、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用 synchronized(x){ x.notify() //或者wait() } 四、sleep必须捕获异常,而wait,notify和notifyAll不须要捕获异常
14.线程同步的几种方式
一、synchronized 能够用来同步方法和同步代码块 同步方法: 给一个方法增长synchronized关键字,能够是静态方法(锁住整个类),也能够是非静态方法(不能是抽象方法) 同步代码块:经过锁定一个指定的对象,来对同步代码块进行同步 同步是高开销操做,尽可能少使用同步方法,同步关键代码的代码块便可 二、Lock锁 ReentrantLock 代码经过lock()方法获取锁,但必须调用unlock()方法释放锁
15.如何理解线程池思想?Java中的线程池有哪些?
建立线程每次都要和操做系统进行交互,线程执行完任务后就会销毁,若是频繁的大量去建立线程确定会形成系统资源开销很大,下降程序的运行效率。 线程池思想就很好的解决了频繁建立线程的问题,咱们能够预先建立好一些线程,把他们放在一个容器中,须要线程执行任务的时候,从容器中取出线程,任务执行完毕后将线程在放回容器。 一:newCachedThreadPool(缓存线程型池) 二:newFixedThreadPool (缓存线程池和上面的差很少) 三:ScheduledThreadPool(调度型线程池) 四:SingleThreadExecutor(单例线程) 详细解析网址:https://zhidao.baidu.com/question/1116515319242598059.html
16.开发中哪些地方用到了多线程?
你能够说咱们的作的项目几乎没有用到多线程 , 涉及到的地方几乎都用 搭建集群的方式来解决
17.什么是线程同步、异步?
1.线程同步:是多个线程同时访问同一资源,等待资源访问结束,浪费时间,效率不高 2.线程异步:访问资源时,若是有空闲时间,则可在空闲等待同时访问其余资源,实现多线程机制
18.说一下 Runnable和 Callable有什么区别?
1)Runnable接口中的方法没有返回值;Callable接口中的方法有返回值 2)Runnable接口中的方法没有抛出异常;Callable接口中的方法抛出了异常 3)Runnable接口中的落地方法是call方法;Callable接口中的落地方法是run方法 详细连接:https://blog.csdn.net/syc0616/article/details/109734464
19.线程的run()和start()的区别
1.调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,没法达到启动多线程的目的,至关于主线程线性执行 Thread 对象的 run() 方法。 2.一个线程对线的 start() 方法只能调用一次,屡次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。
20.synchronized和lock的区别
做用位置不一样: synchronized能够给方法、代码块加锁 lock只能给代码块加锁 锁的获取和释放机制不一样: synchronized无需手动获取锁和释放锁,发生异常会自动解锁,不会出现死锁 修饰成员方法时,默认的锁对象,就是当前对象 修饰静态方法时,默认的锁对象,是当前类的Class对象 好比:Person.class 修饰代码块时,能够本身来设置锁对象,好比: synchronized(this){ //线程进入,自动获取到锁 //线程执行结束,自动释放锁 } lock须要本身加锁和释放锁,如lock()和unlock(),若是忘记使用unlock(),则会出现死锁,因此通常会在finally里使用unlock()
21.乐观锁和悲观锁的区别
1.悲观锁假定会发生冲突,访问的时候都要先得到锁,保证同一个时刻只有线程得到锁,读读也会阻塞 就是在操做数据时,认为此操做会出现数据冲突,因此在进行每次操做时都要经过获取锁才能进行对相同数据的操做,这点跟java中的synchronized很类似,因此悲观锁须要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库本身实现了的,要用的时候,咱们直接调用数据库的相关语句就能够了 二、乐观锁假定不会发生冲突,只有在提交操做的时候检查是否有冲突 主要适用场景:当要更新一条记录的时候,但愿这条记录没有被别人更新,也就是说实现线程安全的数据更新 实现方式: (1)取出记录时,获取当前version (2)更新时,带上这个version (3)执行更新时, set version = newVersion where version = oldVersion (4)若是version不对,就更新失败 详情网址:https://www.cnblogs.com/qlqwjy/p/7798266.html
22.多线程怎么解决高并发?
1.synchronized 关键字主要解决多线程共享数据同步问题。 2.ThreadLocal 使用场合主要解决多线程中数据因并发产生不一致问题。 3.ThreadLocal 和 Synchonized 都用于解决多线程并发访问可是 ThreadLocal 与 synchronized 有本质的区别: ( (1)synchronized 是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。 (2) ThreadLocal 是为每个线程都提供了变量的副本,使得每一个线程在某一时间访问到的并 不是同一个对象,这样就隔离了多个线程对数据的数据共享。而 Synchronized 却正好相反, 它用于在多个线程间通讯时可以得到数据共享。
23.Java 关键字 volatile 与 synchronized 做用与区别?
1.volatile 它所修饰的变量不保留拷贝,直接访问主内存中的。 在Java内存模型中,有main memory,每一个线程也有本身的memory (例如寄存器)。为了性能,一个线程会在本身的memory中保持要访问的变量的副本。这样就会出现同一个变 量在某个瞬间,在一个线程的memory中的值可能与另一个线程memory中的值,或者main memory中的值不一致的状况。 一个变量声明为volatile,就意味着这个变量是随时会被其余线程修改的,所以不能将它cache在线程memory中。 2.synchronized 当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多只有一个线程执行该段代码。 区别: 1、volatile是变量修饰符,而synchronized则做用于一段代码或方法。 2、volatile只是在线程内存和“主”内存间同步某个变量的值;而synchronized经过锁定和解锁某个监视器同步全部变量的值。显然synchronized要比volatile消耗更多资源。
24.什么是 Java Timer 类?如何建立一个有特定时间间隔的任务?
java.util.Timer 是一个工具类.能够用于安排一个线程在将来的某个特定时间 执行。Timer 类能够用安排一次性任务或者周期任务。 java.util.TimerTask 是一个实现了 Runnable 接口的抽象类.咱们须要去继承 这个类来建立咱们本身的定时任务并使用 Timer 去安排它的执行。
25.怎么检测一个线程是否拥有锁?
在 java.lang.Thread 中有一个方法叫 holdsLock(),它返回 true 若是当且仅当当 前线程拥有某个具体对象的锁
**26.Java 中的锁有几种方式 **
两种: Synchronized Lock 1.Synchronized的局限性: 1.若是这个获取锁的线程因为要等待IO或者其余缘由(好比调用sleep方法)被阻塞了,但 是又没有释放锁,其余线程便只能干巴巴地等待。(不能主动释放锁) 2.当有多个线程读写文件时,读操做和写操做会发生冲突现象,写操做和写操做会发生冲突 现象,可是读操做和读操做不会发生冲突现象若是多个线程都只是进行读操做,因此当一个 线程在进行读操做时,其余线程只能等待没法进行读操做。(不分状况,一概锁死) 2.Lock 的几个实现类 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁” ReadWriteLock,顾名思义,是读写锁。它维护了一对相关的锁 — — “读取锁”和“写入 锁”,一个用于读取操做,另外一个用于写入操做。他的两个实现类读锁readerLock和写锁 writerLock。