多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。java
就是在单个程序中同时运行多个线程来完成不一样的工做。面试
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
3.程序、进程与多线程
1)程序
Java源程序和字节码文件被称为“程序” (Program),是一个静态的概念。安全
2) 进程
是指一个内存中运行的应用程序,每一个进程都有一个独立的内存空间,一个应用程序能够同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序便是一个进程从建立、运行到消亡的过程。多线程
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是能够有多个线程的,这个应用程序也能够称之为多线程程序。并发
Java使用java.lang.Thread类表明线程,全部的线程对象都必须是Thread类或其子类的实例。ide
Java中经过继承Thread类来建立并启动多线程的步骤以下:测试
定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就表明了线程须要完成的任务,所以把run()方法称为线程执行体
建立Thread子类的实例,即建立了线程对象
调用线程对象的start()方法来启动该线程
自定义线程类:this
public class MyThread extends Thread { //定义指定线程名称的构造方法 public MyThread(String name) { //调用父类的String参数的构造方法,指定线程的名称 super(name); } /** * 重写run方法,完成该线程执行的逻辑 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+":正在执行!"+i); } } }
测试类:线程
public class Demo01 { public static void main(String[] args) { //建立自定义线程对象 MyThread mt = new MyThread("新的线程!"); //开启新线程 mt.start(); //在主方法中执行for循环 for (int i = 0; i < 10; i++) { System.out.println("main线程!"+i); } } }
构造方法:3d
public Thread():分配一个新的线程对象。
public Thread(String name):分配一个指定名字的新的线程对象。
public Thread(Runnable target):分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
经常使用方法:
public String getName():获取当前线程名称。
public void start():致使此线程开始执行;Java虚拟机调用此线程的run方法。
public void run():此线程要执行的任务在此处定义代码。
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时中止执行)。
public static Thread currentThread():返回对当前正在执行的线程对象的引用
采用java.lang.Runnable也是很是常见的一种,咱们只须要重写run方法便可。
步骤以下:
定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体一样是该线程的线程执行体。
建立Runnable实现类的实例,并以此实例做为Thread的target来建立Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动线程。
自定义线程类:
public class MyRunnable implements Runnable { @Override public void run(){ for(inti=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+""+i); } } }
测试类:
public class Demo{ public static void main(String[]args){ //建立自定义类对象线程任务对象 MyRunnable mr = new MyRunnable(); //建立线程对象 Thread t = new Thread(mr,"小强"); t.start(); for(inti=0;i<20;i++){ System.out.println("旺财"+i); } } }
实现Runnable接口比继承Thread类所具备的优点:
适合多个相同的程序代码的线程去共享同一个资源。
能够避免java中的单继承的局限性。
增长程序的健壮性,实现解耦操做,代码能够被多个线程共享,代码和线程独立。
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
/** * 用Lambda表达式来建立多线程 */ public class Thread004Lambda { public static void main(String[] args) { Thread t = new Thread(()->{ System.out.println("我是一个线程对象"); }); t.start();//启动线程 System.out.println("我是主线程"); } }
1.线程的五种状态
新生状态: new
就绪状态: runnable
运行状态: running
阻塞状态: blocked
执行完毕: dead
死亡状态是线程生命周期中的最后一个阶段。线程死亡的缘由有两个。一个是正常运行的线程完成了它的所有工做;另外一个是线程被强制性地终止,如经过执行stop或destroy方法来终止一个线程。
不要调用 stop destory 方法 ,太暴力,一盆冷水让其中止。
自定义类:
/** * * 终止线程的套路 * 一、加入标识 * 二、线程体的执行须要依赖标识 * 三、对外提供一个能够修改标识的方法 * 四、外部适当的时机调用方法修改标识 */ class MyThread03 implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while (true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("在和小美聊天,在说第"+(i++)+"句话"); if(!flag){ break; } } } public void stopThread(){ flag = false; } }
测试类:
public class Thread005 { public static void main(String[] args) throws Exception{ MyThread03 mt = new MyThread03(); Thread t = new Thread(mt); t.start();//开启线程 Thread.sleep(2000); mt.stopThread(); } }
sleep方法:Sleep时别的线程也不能够访问锁定对象。
yield方法: 让出CPU的使用权,从运行态直接进入就绪态。让CPU从新挑选哪个线程进入运行状态。
join方法: 等待当前线程死亡,当某个线程等待另外一个线程执行结束后,才继续执行时,使用另外一个线程的join方法。使调用该方法的线程在此以前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行
一个线程的默认优先级是5
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
注意:优先级低只是意味着得到调度的几率低。并非绝对先调用优先级高后调用优先级低的线程。
自定义类:
class FatherThread implements Runnable { @Override public void run() { System.out.println("爸爸想抽烟,发现烟抽完了"); System.out.println("爸爸让儿子去买包红塔山"); Thread son = new Thread(new SonThread()); son.start(); // 启动新的线程 System.out.println("爸爸等儿子买烟回来"); try { son.join(); // 一直傻傻地等 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("能够抽烟了"); } } class SonThread implements Runnable { public void run() { System.out.println("儿子出门去买烟"); System.out.println("儿子买烟须要10分钟"); try { for (int i = 1; i <=10;i++) { System.out.println(Thread.currentThread().getName() + " 第" + i + "分钟"); Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 儿子买烟回来了"); } }
测试类:
public class Demo008Thread { public static void main(String[] args)throws Exception { Thread t1 = new Thread(new SonThread(),"t1"); Thread t2 = new Thread(new SonThread(),"t2"); // int p = t1.getPriority();获取优先级 t1.setPriority(10);//设置线程优先级 t2.setPriority(1); t1.start(); t2.start(); Thread.sleep(1000); //System.out.println(t.isAlive()); // 查看线程是否还活着 } }
当咱们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操做,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制(synchronized)来解决。
同步代码块:synchronized关键字能够用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){ 须要同步操做的代码 }
同步锁:
对象的同步锁只是一个概念,能够想象为在对象上标记了一个锁.
锁对象能够是任意类型。
多个线程对象要使用同一把锁。
同步方法:使用synchronized修饰的方法,就叫作同步方法,保证A线程执行该方法的时候,其余线程只能在方法外等着。
格式:
public synchronized void method(){ 可能会产生线程安全问题的代码 } 1 2
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,咱们使用当前方法所在类的字节码对象(类名.class)。
综合案例:
/** * 帐户类 */ class Account { String aname; // 帐户名 int money; // 帐户余额 public Account(String aname, int money) { this.aname = aname; this.money = money; } }
class Drawing implements Runnable{ Account account = null; int money = 0; // 准备取钱的金额 int acturalMoney = 0; // 实际取走的钱 public Drawing(Account account, int money) { this.account = account; this.money = money; } @Override public void run() { synchronized (account){//同步代码块 // 若是余额不够, 取不了钱 if (account.money < money) { System.out.println(" 余额不足, 没去走钱"); return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } account.money -= money; // 取了钱,余额变少 acturalMoney = money; // 取出来的钱 System.out.println(Thread.currentThread().getName() + "取了 " + acturalMoney + " 还剩 " + account.money); } } }
测试类:
/** * 两我的操做同一个帐户 */ public class Demo010Thread { public static void main(String[] args) throws Exception{ // 开户 Account account = new Account("高富帅",100); System.out.println(account.money); Drawing d = new Drawing(account,90); // 你去取钱 Thread t = new Thread(d,"你" ); Thread t1 = new Thread(d,"女友" ); t.start(); t1.start(); Thread.sleep(1500); System.out.println(account.money); } }
###4.死锁及解决方案
1)死锁的概念
死锁是指两个或两个以上的进程在执行过程当中,因为竞争资源或者因为彼此通讯而形成的一种阻塞的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
如何解决死锁问题:
每每是程序逻辑的问题。须要修改程序逻辑。尽可能不要同时持有两个对象锁。