前言
过程:一个计算机程序的运行实例,蕴含了须要执行的指令;有本人的独立地址空间,蕴含程序内容和数据;不同过程的地址空间是相互隔离的;过程领有各种资源和状态信息,包含关上的文件、子过程和信号处理。
线程:示意程序的执行流程,是CPU调度执行的根本单位;线程有本人的程序计数器、寄存器、堆栈和帧。同一过程中的线程共用雷同的地址空间,同时共享进过程锁领有的内存和其余资源。
多线程的实现
继承Thread类
- 创立一个类,这个类须要继承Thread类
- 重写Thread类的run办法(run办法中是业务代码)
- 实例化此线程类
- 调用实例化对象的start办法启动线程
package com.test;public class Demo1 { public static void main(String[] args){ ThreadDemo threadDemo = new ThreadDemo(); threadDemo.start(); }}class ThreadDemo extends Thread{ @Override public void run() { System.out.println("运行了run办法"); }}
在多线程编程中,代码的执行后果与代码的执行程序或者调用程序是无关的线程是一个子工作,CPU以不确定的形式或者是以随机的工夫来调用线程中的run办法这体现了线程运行的随机性
package com.test;public class Demo2 { public static void main(String[] args) { Demo2Thread demo2Thread = new Demo2Thread(); /* *demo2Thread.start办法才是启动线程 *demo2Thread.run办法只是由main主线程来调用run办法 */ demo2Thread.start(); try { for (int i = 0; i < 3; i++) { System.out.println("运行了main办法"); Thread.sleep(100); } }catch (Exception e){ e.printStackTrace(); } }}class Demo2Thread extends Thread{ @Override public void run() { try { for (int i = 0; i < 3; i++) { System.out.println("运行了run办法"); Thread.sleep(100); } }catch (Exception e){ e.printStackTrace(); } }}
start的执行程序和线程的启动程序是不统一的
1,2,3,4,5的输入程序是随机的
package com.test;public class Demo3 { public static void main(String[] args) { Demo3Thread demo3Thread1 = new Demo3Thread(1); Demo3Thread demo3Thread2 = new Demo3Thread(2); Demo3Thread demo3Thread3 = new Demo3Thread(3); Demo3Thread demo3Thread4 = new Demo3Thread(4); Demo3Thread demo3Thread5 = new Demo3Thread(5); demo3Thread1.start(); demo3Thread2.start(); demo3Thread3.start(); demo3Thread4.start(); demo3Thread5.start(); }}class Demo3Thread extends Thread{ private int i; public Demo3Thread(int i){ this.i = i; } @Override public void run() { System.out.println("i=" + i); }}
实现Runnable接口
1)创立一个类,整个类须要实现Runnable接口
2)重写Runnable接口的run办法
3)实例化创立的这个类
4)实例化一个Thread类,把第3步实例化创立的对象通过Thread类的构造方法传递给Thread类
5)调用Thread类的run办法
package com.test;public class Demo4 { public static void main(String[] args) { Demo4Thread thread = new Demo4Thread(); Thread t = new Thread(thread); t.start(); System.out.println("运行了main办法"); }}class Demo4Thread implements Runnable{ @Override public void run() { System.out.println("运行了run办法"); }}
应用继承Thread类的形式开发多线程应用程序是有局限的,因为Java是单继承,继承了Thread类就无奈继承其余类,所以为了扭转这种局限,用实现Runnable接口的形式来实现多线程
成员变量与线程平安
自定义线程类中的成员变量对于其余线程能够是共享或者不共享的,这对于多线程的交互很重要
- 不共享数据时
package com.test;public class Demo5 { public static void main(String[] args) { Thread t1 = new Demo5Thread(); Thread t2 = new Demo5Thread(); Thread t3 = new Demo5Thread(); t1.start(); t2.start(); t3.start(); }}class Demo5Thread extends Thread{ private int i = 5; @Override public void run() { while(i > 0){ i--; System.out.println(Thread.currentThread().getName() + " i = " + i); } }}
每个线程都有各自的i变量,i变量的值相互之间不影响
- 共享数据时
package com.test;public class Demo6 { public static void main(String[] args) { Thread t = new Demo6Thread(); /* 为什么能将Thread类的对象传递给Thread类? 因为Thread类自身就实现了Runnable接口 */ Thread thread1 = new Thread(t); Thread thread2 = new Thread(t); Thread thread3 = new Thread(t); Thread thread4 = new Thread(t); Thread thread5 = new Thread(t); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread5.start(); }}class Demo6Thread extends Thread{ private int i = 5; @Override public void run() { i--; System.out.println(Thread.currentThread().getName() + " i = " + i); }}
共享数据时,将数据所在类的对象传递给多个Thread类即可
共享数据有概率呈现不同线程产生雷同的i的值,这就是非线程平安
线程罕用API
- currentThread办法
返回代码被哪个线程调用的详细信息
package com.test;public class Demo7 { public static void main(String[] args) { //main线程调用Demo7Thread的构造方法 Thread thread = new Demo7Thread(); //Thread-0线程调用run办法 thread.start(); System.out.println("main办法" + Thread.currentThread().getName()); }}class Demo7Thread extends Thread{ public Demo7Thread(){ System.out.println(Thread.currentThread().getName() + "的构造方法"); } @Override public void run() { System.out.println(Thread.currentThread().getName() + "的run办法"); }}
输入后果为:
main的构造方法
main办法main
Thread-0的run办法
main办法会被名称为main的线程调用,在新建线程类的对象时,线程类的构造方法会被main线程调用;
线程类对象的start办法会调用run办法,此时线程类默认的名称为Thread-0
- isAlive办法
判断以后的线程是否处于流动的状态,活动状态就是线程曾经启动并且没有完结运行的状态
package com.test;public class Demo8 { public static void main(String[] args) { Thread t = new Demo8Thread(); System.out.println("线程启动前:" + t.isAlive()); t.start(); System.out.println("线程启动后:" + t.isAlive()); }}class Demo8Thread extends Thread{ @Override public void run() { System.out.println("run办法的运行状态" + this.isAlive()); }}
输入后果为:
线程启动前:false
线程启动后:true
run办法的运行状态true
true示意线程正处于活动状态,false则示意线程正处于非活动状态
- sleep办法
使以后正在执行的线程在指定的毫秒数内暂停执行
package com.test;public class Demo8 { public static void main(String[] args) { Thread t = new Demo8Thread(); System.out.println("线程启动前工夫:" + System.currentTimeMillis()); t.start(); System.out.println("线程启动后工夫:" + System.currentTimeMillis()); }}class Demo8Thread extends Thread{ @Override public void run() { System.out.println("线程sleep前的工夫:" + System.currentTimeMillis()); try { Thread.sleep(300); }catch (Exception e){ e.printStackTrace(); } System.out.println("线程sleep后的工夫:" + System.currentTimeMillis()); }}
- getId办法
获取以后线程的惟一标识
package com.test;public class Demo9 { public static void main(String[] args) { Thread t = Thread.currentThread(); System.out.println(t.getName() + ", " + t.getId()); Thread thread = new Thread(); System.out.println(thread.getName() + ", " + thread.getId()); }}
- 进行线程
进行一个线程,即线程在实现工作之前,就完结以后正在执行的操作
1)应用退出标记,使线程失常进行,即run办法运行完后线程终止
package com.test;public class Demo10 { public static void main(String[] args) { Demo10Thread thread = new Demo10Thread(); thread.start(); try { Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } /* stopThread办法,将flag变为false,为什么可能传递到以后线程中? 我感觉是因为以后线程是始终在运行的,while()中的条件始终成立 所以当调用了stopThread办法,将flag变为false,while循环就完结了,run办法中的代码也完结了 所以线程进行了 */ thread.stopThread(); }}class Demo10Thread extends Thread{ private Boolean flag = true; @Override public void run() { try { while (flag){ System.out.println("线程正在运行"); Thread.sleep(1000); } System.out.println("线程完结运行"); }catch (Exception e){ e.printStackTrace(); } } public void stopThread(){ flag = false; }}
2)stop办法强制完结线程
package com.test;public class Demo11 { public static void main(String[] args) { Demo11Thread thread = new Demo11Thread(); thread.start(); try { Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } //stop办法中的斜杠示意办法曾经被作废,不倡议应用此办法 thread.stop(); }}class Demo11Thread extends Thread{ private Boolean flag = true; @Override public void run() { try { while (flag){ System.out.println("线程正在运行~~~"); Thread.sleep(1000); } System.out.println("线程完结运行~~~"); }catch (Exception e){ e.printStackTrace(); }catch (ThreadDeath e){//捕捉线程终止的异样 System.out.println("进入catch块"); e.printStackTrace(); } }}
stop强制进行线程可能使一些清感性的工作得不到实现;还会对锁定的对象进行解锁,使数据得不到同步的解决,导致数据不统一
3)interrupt办法中断线程
package com.test;public class Demo12 { public static void main(String[] args) { Demo12Thread thread = new Demo12Thread(); thread.start(); thread.interrupt(); System.out.println("thread线程是否曾经进行?" + thread.isInterrupted() + ", " + thread.getName()); System.out.println("以后线程是否曾经进行?" + Thread.interrupted() + ", " + Thread.currentThread().getName()); }}class Demo12Thread extends Thread{ @Override public void run() { for(int i = 0; i < 5; i++){ System.out.println(i); } }}
调用interrupt办法不会真正的完结线程,而是给以后线程打上一个进行的标记
Thread类提供了interrupt办法测试以后线程是否曾经中断,isInterrupted办法测试线程是否曾经中断
执行后果为:
thread线程是否曾经进行?true, Thread-001234以后线程是否曾经进行?false, main
thread.isInterrupted办法查看线程类是否被打上进行的标记,Thread.interrupted办法查看主线程是否被打上进行的标记
暂停线程
暂停线程应用suspend办法,重启暂停线程应用resume办法
suspend办法暂停线程后,i的值就不会持续减少。两次"第一次suspend"输入的后果统一
resume办法重启暂停线程后,i的值会持续减少,再应用suspend办法暂停线程,两次"resume后第二次suspend:"输入的后果统一
package com.test;public class Demo13 { public static void main(String[] args) throws InterruptedException { Demo13Thread thread = new Demo13Thread(); thread.start(); Thread.sleep(100); thread.suspend(); System.out.println("第一次suspend:" + thread.getI()); Thread.sleep(100); System.out.println("第一次suspend:" + thread.getI()); thread.resume(); Thread.sleep(100); thread.suspend(); System.out.println("resume后第二次suspend:" + thread.getI()); Thread.sleep(100); System.out.println("resume后第二次suspend:" + thread.getI()); }}class Demo13Thread extends Thread{ private long i; public long getI() { return i; } public void setI(long i) { this.i = i; } @Override public void run() { while (true){ i++; } }}
suspend办法会使线程独占公共的同步对象,使其余线程无法访问公共的同步对象
suspend办法还可能会造成共享对象的数据不同步
yield办法
yield办法是使以后线程放弃CPU资源,将资源让给其余的线程,然而放弃的工夫不确定,可能刚刚放弃,马上又获取CPU工夫片
package com.test;public class Demo15 { public static void main(String[] args) { Demo15Thread thread = new Demo15Thread(); thread.start(); }}class Demo15Thread extends Thread{ @Override public void run() { long start = System.currentTimeMillis(); int count = 0; for(int i = 0; i < 50000; i++){ Thread.yield();//使以后线程放弃CPU资源,然而放弃的工夫不确定 count = count + i; } long end = System.currentTimeMillis(); System.out.println("破费工夫:" + (end - start)); }}
线程的优先级
在操作系统中,线程是能够划分优先级的,优先级较高的线程可能失去更多的CPU资源,即CPU会优先执行优先级较高的线程对象中的工作。设置线程优先级有助于帮忙"线程调度器"确定下一次抉择哪个线程优先执行
设置线程的优先级应用setPriority办法,优先级分为1~10级,如果设置的优先级小于1或者大于10,JVM会抛出IllegalArgumentException异样,JDK默认设置了3个优先级常量,MIN_PRIORITY=1(最小值),NORM_PRIORITY=5(两头值,也是默认值),MAX_PRIORITY=10(最大值)
获取线程的优先级应用getPriority办法
package com.test;public class Demo16 { public static void main(String[] args) { System.out.println("主线程的运行优先级是:" + Thread.currentThread().getPriority()); System.out.println("设置主线程的运行优先级"); Thread.currentThread().setPriority(8); System.out.println("主线程的运行优先级是:" + Thread.currentThread().getPriority()); /* 线程的优先级具备继承性,原本默认的线程的优先级为5 然而将主线程的优先级设置为8,此子线程也会继承主线程的优先级8 */ Thread t = new Demo16Thread(); t.start(); }}class Demo16Thread extends Thread{ @Override public void run() { System.out.println("线程的优先级是:" + this.getPriority()); }}
优先级较高的线程,先执行的概率较大
线程的同步机制
Java多线程中的同步,指的是如何在Java语言中开发出线程平安的程序,或者如何在Java语言中解决线程不平安时所带来的问题
"线程平安"与"非线程平安"是多线程技术中的经典问题。"非线程平安"就是当多个线程拜访同一个对象的成员变量时,读取到的数据可能是被其余线程批改过的(脏读)。"线程平安"就是获取的成员变量的值是通过同步解决的,不会有脏读的景象
synchronized同步办法
局部变量是线程平安的
局部变量不存在线程平安的问题,永远都是线程平安的,这是由局部变量是公有的个性造成的
package com.test.chap2;public class Demo1 { public static void main(String[] args) { Service service = new Service(); ThreadDemo1 t1 = new ThreadDemo1(service); t1.start(); ThreadDemo2 t2 = new ThreadDemo2(service); t2.start(); }}class Service { public void add(String name){ int number = 0;//number是办法内的局部变量 if("a".equals(name)){ number = 100; System.out.println("传入的参数为a,批改number的值为:" + number); try { //这里使线程休眠是为了期待其余线程批改number的值 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }else { number = 200; System.out.println("传入的参数不为a,批改number的值为:" + number); } }}class ThreadDemo1 extends Thread{ private Service service; public ThreadDemo1(Service service){ this.service = service; } @Override public void run() { service.add("a"); }}class ThreadDemo2 extends Thread{ private Service service; public ThreadDemo2(Service service){ this.service = service; } @Override public void run() { service.add("b"); }}
成员变量不是线程平安的
如果有两个线程,都要操作业务对象中的成员变量,可能会产生"非线程平安"的问题,此时须要在办法前应用synchronized关键字进行润饰
number是Demo2Service类的成员变量,Demo2Service类的add办法中,当传入的参数为a时,会进入if条件,休眠1s,并将number的值改为100,当传入的参数不为a时,不会休眠,将number的值改为200
t3线程,传入的参数为a;t4线程,传入的参数为b,所以在线程start之后,t3线程会休眠1s,t4线程不会休眠,所以t4线程会先将number的值改为200并输入,然而当t3线程完结休眠后,输入的number的值也是200,这就产生了线程平安的问题
为了解决此线程不平安的问题,能够在办法前,加上synchronized关键字进行润饰,此时调用此办法的线程须要执行完,办法才会被另一个线程所调用
package com.test.chap2;public class Demo2 { public static void main(String[] args) { Demo2Service service = new Demo2Service(); ThreadDemo3 t3 = new ThreadDemo3(service); t3.start(); ThreadDemo4 t4 = new ThreadDemo4(service); t4.start(); }}class Demo2Service{ private int number = 0; public void add(String name){ if("a".equals(name)){ number = 100; try { //这里使线程休眠是为了期待其余线程批改number的值 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("传入的参数为a,批改number的值为:" + number); }else { number = 200; System.out.println("传入的参数不为a,批改number的值为:" + number); } }}class ThreadDemo3 extends Thread{ private Demo2Service service = new Demo2Service(); public ThreadDemo3(Demo2Service service){ this.service = service; } @Override public void run() { service.add("a"); }}class ThreadDemo4 extends Thread{ private Demo2Service service; public ThreadDemo4(Demo2Service service){ this.service = service; } @Override public void run() { service.add("b"); }}
多个对象应用多个对象锁
synchronized设置的锁都是对象锁,而不是将代码或者办法作为锁
当多个线程拜访同一个对象时,哪个线程先执行此对象带有synchronized关键字润饰的办法,其余线程就只能处于期待状态,直到此线程执行结束,开释了对象锁,其余线程能力继续执行
如果多个线程别离拜访多个对象,JVM会创立出多个对象锁,此时每个线程之间都不会相互烦扰
锁的主动开释
当一个线程执行的代码呈现了异样,其持有的锁会主动开释
synchronized同步语句块
synchronized关键字润饰的办法的不足之处
如果线程A和线程B都拜访被synchronized关键字润饰的get办法,线程B就必须等线程A执行完后,能力执行,这样运行的效率低
synchronized同步代码块的应用
同步代码块的作用与在办法上增加synchronized关键字润饰的作用是一样的
t1和t2两个线程同时拜访Demo10Service的synTest办法,synTest办法中局部代码加上了同步代码块,从输入后果能够发现,t1和t2线程会同时拜访synTest办法并同时执行非同步代码块的逻辑,然而同步代码块的局部,t1线程先拜访的话,t2线程就必须等到t1线程执行结束后,能力继续执行
如果在synTest办法上加上synchronized关键字润饰,t1线程先拜访synTest办法的话,t2线程就必须等到t1线程执行结束后,才会拜访synTest办法并执行,总的来说,同步代码块能够锁住局部须要同步执行的代码,而办法中没有锁住的其余代码能够异步执行
package com.test.chap2;public class Demo10 { public static void main(String[] args) throws InterruptedException { Demo10Service service = new Demo10Service(); Thread t1 = new Demo10Thread(service); t1.setName("A"); Thread t2 = new Demo10Thread(service); t2.setName("B"); t1.start(); t2.start(); }}class Demo10Service{ public void synTest(){ System.out.println(Thread.currentThread().getName() + "线程拜访synTest办法"); try { synchronized (this) { System.out.println(Thread.currentThread().getName() + "线程开始~~~"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "线程完结~~~"); } } catch (InterruptedException e) { e.printStackTrace(); } }}class Demo10Thread extends Thread{ Demo10Service service; public Demo10Thread(Demo10Service service){ this.service = service; } @Override public void run() { service.synTest(); }}
volatile关键字
volatile关键字的次要作用是使变量在多个线程之间可见
当线程启动后,如果flag变量前没有volatile关键字润饰,线程会始终卡在run办法中的while循环中,批改flag的值不会失效,而加了volatile关键字润饰后,批改flag的值会失效,线程会退出while循环
在启动线程时,flag变量存在于公共堆栈及线程的公有堆栈中。JVM为了线程的运行效率,始终从公有堆栈中取flag的值,当执行service.flag = false语句时,尽管批改了flag的值,然而批改的却是公共堆栈的flag值,线程还是从公有堆栈中取flag的值,所以并不会退出while循环。应用volatile关键字润饰成员变量后,会强制JVM从公共堆栈中获取变量的值,所以可能退出while循环
package com.test.chap2;public class Demo { public static void main(String[] args) throws InterruptedException { DemoService service = new DemoService(); Thread t1 = new Thread(service); t1.start(); Thread.sleep(100); System.out.println("筹备批改flag的值"); service.flag = false; System.out.println(service.flag); }}class DemoService extends Thread{ //没有volatile关键字的话,线程会统一处于while循环中 volatile public boolean flag = true; @Override public void run() { System.out.println("开始运行run办法"); while (flag){ } System.out.println("完结运行run办法"); }}
synchronized和volatile的区别:
1、volatile是线程同步的轻量级实现,所以volatile的性能要比synchronized要好,然而volatile只能润饰变量。而synchronized能够润饰办法以及代码块。随着JDK的版本更新,synchronized在执行效率上也有很大的晋升,使用率还是较高
2、多线程拜访volatile不会阻塞,而拜访synchronized会呈现阻塞
3、volatile能保证数据的可见性,然而不能保障原子性,可能会呈现脏读;而synchronized可能保障原子性,也能间接保障可见性,因为其能将公有内存和公共内存中的数据做同步
4、volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间拜访资源的同步性
最初
大家看完有什么不懂的能够在下方留言探讨,也能够关注我私信问我,我看到后都会答复的。也欢送大家关注我的公众号:前程有光,马上金九银十跳槽面试季,整顿了1000多道将近500多页pdf文档的Java面试题材料放在外面,助你圆梦BAT!文章都会在外面更新,整顿的材料也会放在外面。谢谢你的观看,感觉文章对你有帮忙的话记得关注我点个赞反对一下!