系统资源
内存、cup、总线
进程
运行中的程序,该程序对拥有的系统资源的逻辑描述。
多进程
操作系统可以同时运行多个程序。
线程
实际存在的,执行任务的一个顺序功能流,本身是不具有系统资源的,只能使用分配给程序的系统资源。
多线程
同一个进程中,多个功能流同时执行。
目的:最大限度使用CPU资源
- 线程的2种启动方式
1.继承Thread类,重写run方法
public class test {
public static void main(String[] args) throws Exception {
new MyThread().start();//启动线程
}
}
class MyThread extends Thread{
public void run(){
System.out.println("MyThread Run");
}
}
2.实现Runnable接口
public class test {
public static void main(String[] args) throws Exception {
new Thread(new PrimeRun()).start();
}
}
class PrimeRun implements Runnable{
public void run(){
System.out.println("PrimeRun");
}
}
线程生命周期
简述上面的类
1.多个线程都想持有一个对象的同步锁的时候,只能有一个线程会持有,其他线程将在该对象的锁池中等待,拿到锁后,又回到就绪状态,等待被cpu调度
2.当对象在同步线程中调用wait()方法,该线程就会在该对象的等待池中等待被唤醒,唤醒后又去锁池中,为获得同步锁,后续和上面一样
1.创建状态
新建的一个Runnable对象
2.就绪
进入调度池中,等待系统调度
3.阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。退出阻塞状态,会重新进入就绪状态
4.运行状态
获得cpu时间片,执行run方法
①、线程调用sleep方法主动放弃所占用的系统资源
②、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
③、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
④、线程在等待某个通知(notify)
⑤、程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法
5.销亡状态
run()方法执行完成,某些强制退出手段。死亡了不可再调用start()方法,可能Thread对象对象还存在内存中。
线程睡眠 - sleep
该方法是一个静态方法,作用是将当前线程睡眠。并不会释放线程锁持有的锁
new Thread(new Runnable(){
public void run(){
for (int i=0;i<100;i++){
System.out.println("current Thread Sleep:"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
打印间隔为1秒,使用sleep方法生命周期状态变化如下
运行 -> 阻塞 -> 就绪(等待cpu调度)
这个调度是系统控制,所以上面说的打印间隔1秒,是有误差的,应该是大于1秒
线程让步 - yield
该方法也是静态方法,作用是让当前线程从运行状态进入就绪状态
class MyThread extends Thread {
public MyThread(String name, int pro) {
super(name);// 设置线程的名称
this.setPriority(pro);// 设置优先级
}
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("线程优先级"+this.getPriority()+this.getName() + "线程第" + i + "次执行!");
if (i % 5 == 0)
Thread.yield(); //一个线程最多执行5次,但是完全有可能进入就绪状态,马上又获得cpu调度(这个是不可控的)
}
}
}
test
new MyThread("低级", 1).start();
new MyThread("中级", 5).start();
new MyThread("高级", 10).start();
结果:
线程优先级1低级线程第0次执行!
线程优先级10高级线程第0次执行!
线程优先级5中级线程第0次执行!
线程优先级10高级线程第1次执行!
线程优先级1低级线程第1次执行!
线程优先级1低级线程第2次执行!
线程优先级1低级线程第3次执行!
线程优先级1低级线程第4次执行!
线程优先级1低级线程第5次执行!
线程优先级10高级线程第2次执行!
线程优先级10高级线程第3次执行!
线程优先级10高级线程第4次执行!
线程优先级10高级线程第5次执行!
线程优先级5中级线程第1次执行!
线程优先级10高级线程第6次执行!
线程优先级5中级线程第2次执行!
线程优先级5中级线程第3次执行!
线程优先级1低级线程第6次执行!
线程优先级1低级线程第7次执行!
线程优先级5中级线程第4次执行!
线程优先级5中级线程第5次执行!
线程优先级10高级线程第7次执行!
线程优先级5中级线程第6次执行!
线程优先级5中级线程第7次执行!
线程优先级1低级线程第8次执行!
线程优先级5中级线程第8次执行!
线程优先级5中级线程第9次执行!
线程优先级10高级线程第8次执行!
线程优先级5中级线程第10次执行!
线程优先级1低级线程第9次执行!
线程优先级5中级线程第11次执行!
线程优先级5中级线程第12次执行!
线程优先级5中级线程第13次执行!
线程优先级10高级线程第9次执行!
线程优先级10高级线程第10次执行!
线程优先级10高级线程第11次执行!
线程优先级10高级线程第12次执行!
线程优先级10高级线程第13次执行!
线程优先级10高级线程第14次执行!
线程优先级10高级线程第15次执行!
线程优先级5中级线程第14次执行!
线程优先级1低级线程第10次执行!
线程优先级1低级线程第11次执行!
线程优先级1低级线程第12次执行!
线程优先级5中级线程第15次执行!
线程优先级10高级线程第16次执行!
线程优先级5中级线程第16次执行!
线程优先级5中级线程第17次执行!
线程优先级1低级线程第13次执行!
线程优先级5中级线程第18次执行!
线程优先级10高级线程第17次执行!
线程优先级10高级线程第18次执行!
线程优先级5中级线程第19次执行!
线程优先级1低级线程第14次执行!
线程优先级1低级线程第15次执行!
线程优先级10高级线程第19次执行!
线程优先级1低级线程第16次执行!
线程优先级1低级线程第17次执行!
线程优先级1低级线程第18次执行!
线程优先级1低级线程第19次执行!
线程合并 - join
一个对象方法,类似线程依赖,一个线程必须依赖另一个线程执行完成后才可执行。
通俗的说:我加入了你,你该礼貌的让我先执行
class MyThread extends Thread {
public Thread t;
public MyThread(String name) {
super(name);// 设置线程的名称
}
public void run() {
if (this.t != null){
try {
this.t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int i = 0; i < 20; i++) {
System.out.println(this.getName() + "线程第" + i + "次执行!");
}
}
}
test:
MyThread t1 = new MyThread("join线程");
MyThread t2 = new MyThread("被join线程");
t2.t = t1;
t1.start();
t2.start();
执行结果:
join线程线程第0次执行!
join线程线程第1次执行!
join线程线程第2次执行!
join线程线程第3次执行!
join线程线程第4次执行!
join线程线程第5次执行!
join线程线程第6次执行!
join线程线程第7次执行!
join线程线程第8次执行!
join线程线程第9次执行!
join线程线程第10次执行!
join线程线程第11次执行!
join线程线程第12次执行!
join线程线程第13次执行!
join线程线程第14次执行!
join线程线程第15次执行!
join线程线程第16次执行!
join线程线程第17次执行!
join线程线程第18次执行!
join线程线程第19次执行!
被join线程线程第0次执行!
被join线程线程第1次执行!
被join线程线程第2次执行!
被join线程线程第3次执行!
被join线程线程第4次执行!
被join线程线程第5次执行!
被join线程线程第6次执行!
被join线程线程第7次执行!
被join线程线程第8次执行!
被join线程线程第9次执行!
被join线程线程第10次执行!
被join线程线程第11次执行!
被join线程线程第12次执行!
被join线程线程第13次执行!
被join线程线程第14次执行!
被join线程线程第15次执行!
被join线程线程第16次执行!
被join线程线程第17次执行!
被join线程线程第18次执行!
被join线程线程第19次执行!
线程优先级
线程优先级高的只能保证,该线程获得系统资源调度的概率高一些,这个优先级只能描述一个概率,所以是不能以此属性作为线程调度顺序标准的,优先级低的也并非没有机会执行
class MyThread extends Thread {
public MyThread(String name,int priority) {
super(name);// 设置线程的名称
this.setPriority(priority);
}
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(this.getName() + "线程第" + i + "次执行!");
}
}
}
test:
MyThread t1 = new MyThread("1级",1);
MyThread t2 = new MyThread("5级",5);
MyThread t3 = new MyThread("10级",10);
t1.start();
t2.start();
t3.start();
输出结果:
1级线程第0次执行!
1级线程第1次执行!
1级线程第2次执行!
10级线程第0次执行!
10级线程第1次执行!
10级线程第2次执行!
10级线程第3次执行!
10级线程第4次执行!
10级线程第5次执行!
10级线程第6次执行!
10级线程第7次执行!
10级线程第8次执行!
10级线程第9次执行!
10级线程第10次执行!
10级线程第11次执行!
10级线程第12次执行!
10级线程第13次执行!
5级线程第0次执行!
5级线程第1次执行!
5级线程第2次执行!
5级线程第3次执行!
5级线程第4次执行!
5级线程第5次执行!
5级线程第6次执行!
5级线程第7次执行!
5级线程第8次执行!
5级线程第9次执行!
5级线程第10次执行!
5级线程第11次执行!
5级线程第12次执行!
5级线程第13次执行!
5级线程第14次执行!
5级线程第15次执行!
5级线程第16次执行!
5级线程第17次执行!
5级线程第18次执行!
5级线程第19次执行!
10级线程第14次执行!
10级线程第15次执行!
10级线程第16次执行!
10级线程第17次执行!
10级线程第18次执行!
1级线程第3次执行!
10级线程第19次执行!
1级线程第4次执行!
1级线程第5次执行!
1级线程第6次执行!
1级线程第7次执行!
1级线程第8次执行!
1级线程第9次执行!
1级线程第10次执行!
1级线程第11次执行!
1级线程第12次执行!
1级线程第13次执行!
1级线程第14次执行!
1级线程第15次执行!
1级线程第16次执行!
1级线程第17次执行!
1级线程第18次执行!
1级线程第19次执行!
守护线程
守护线程和普通线程使用上并没有什么区别,应用程序的退出不需要关心守护线程是否执行完成
t2.setDaemon(true);//设置为守护线程,只能在创建状态设置该属性,否则抛出异常
守护线程是不需要关心退出时机的,JVM的垃圾回收机制就是一个守护线程,如果应用程序的所有线程都是守护线程JVM虚拟机就会退出
Thread t2 = new Thread(new Runnable(){
public void run(){
for (int i =0 ;i < 999999; i++){
System.out.println("守护线程:" + i);
}
}
});
t2.setDaemon(true);//设置为守护线程
t2.start();
new Thread(new Runnable(){
public void run(){
for (int i =0 ;i <10; i++){
System.out.println("普通线程:" + i);
}
}
}).start();
输出结果:
守护线程:0
守护线程:1
守护线程:2
守护线程:3
守护线程:4
守护线程:5
守护线程:6
守护线程:7
守护线程:8
守护线程:9
守护线程:10
守护线程:11
守护线程:12
守护线程:13
守护线程:14
守护线程:15
守护线程:16
守护线程:17
守护线程:18
守护线程:19
普通线程:0
普通线程:1
普通线程:2
普通线程:3
普通线程:4
普通线程:5
普通线程:6
普通线程:7
普通线程:8
普通线程:9
守护线程:20
线程结束的常用方法
//线程结束的常用方法
new Thread(new Runnable(){
public void run(){
for (int i =0 ;i <10; i++){
if (i == 8) break;//不管用return还是一个boolean控制也好,只要将run方法执行完成即可
System.out.println("普通线程:" + i);
}
}
}).start();
上面的结束一个线程的方法的先决条件就是该线程处于运行时起,当线程处于阻塞期,那么就无法达到退出的效果,就只能巧借线程的interrupt方法,该方法会将处理异常
class MyThread extends Thread{
public void run(){
for (int i =0 ;i <10 ; i++){
try {
System.out.println("-----"+i);
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("中断异常");
return;
}
}
}
public static void main(String[] args) throws Exception {
MyThread t = new MyThread();
t.start();
Thread.sleep(8000);
t.interrupt();
}
输出结果:
-----0
-----1
中断异常
线程同步
warning:多线程最大的问题就是对同一资源的处理
java中的每个对象都有一个同步锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示该对象上锁,此时其他任何线程都无法再去访问该对象的synchronized方法了,直到那个线程执行完毕或者抛出异常,那么僵该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
获得同步锁的对象为this,
public void show() {
synchronized(this){
......
}
}
public synchronized void show() {
...这种方式是上面那种方式的简写。
}
静态方法同步(锁住的是当前类所对应的class对象)
public static synchronized void show() {
....
}
等同于
public static void show() {
synchronized(当前类名.class)
}
经典案例:
我和媳妇同时去银行取钱。
public class test {
public static void main(String[] args) throws Exception {
Account a = new Account(3000);
Thread t1 = new Thread(new GetMoneyRun(a));
Thread t2 = new Thread(new GetMoneyRun(a));
t1.start();
t2.start();
}
}
class Account {
int money;
Account(int money) {
this.money = money;
}
public void getMoney(int a){
this.money -= a;
}
}
class GetMoneyRun implements Runnable {
Account a;
GetMoneyRun(Account a) {
this.a = a;
}
public void run() {
if (a.money > 2000){
try {
Thread.sleep(1000);//这里就是为了模拟一下多线程同时操作同一资源
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a.getMoney(2000);
System.out.println("取现2000");
}else{
System.out.println("所剩余额不够");
}
}
}
输出结果:
取现2000
取现2000
上面这种情况的解决方法就是:
1.对资源(对象)进行上锁
public void run() {
synchronized(a){
if (a.money > 2000){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a.getMoney(2000);
System.out.println("取现2000");
}else{
System.out.println("所剩余额不够");
}
}
}
//输出结果:
取现2000
所剩余额不够
2.对方法进行上锁
public synchronized void getMoney(int a){
if (money > a){
money -= a;
System.out.println("取现:"+a);
}else{
System.out.println("所剩余额不够");
}
}
输出结果:
取现:2000
所剩余额不够
死锁
死锁造成的原因就是对资源的相互持有造成的
public class test {
public static void main(String[] args) throws Exception {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new ChangeObject("线程1",o1,o2);
Thread t2 = new ChangeObject("线程2",o2,o1);
t1.start();
t2.start();
}
}
class ChangeObject extends Thread {
Object o1;
Object o2;
ChangeObject(String threadName,Object o1, Object o2) {
super(threadName);
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized(o1){
try {
System.out.println(this.getName() + " will sleep");
Thread.sleep(1000);
System.out.println(this.getName() + " did wake up");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(o2){
System.out.println("can you access");
}
}
}
}
线程的协调运作
主要用java.lang.Object类中的方法wait()、notify()、notifyAll(),这三个方法必须由上锁的对象调用,所以一定是在同步方法中使用才有意义,调用这三个方法的调用线程必须持有该对象的锁(同步监视器),才可调用这三个方法,否则抛出异常
同步监视器
wait();//导致当前线程等待,并且释放该线程持有的同步锁。直到其他线程的该同步监视器唤醒(notify()、notifyAll())
notify();//唤醒该同步监视器上,等待的线程,多个的话就任选一个。只是被唤醒,需重新争取系统调度
notifyAll();//唤醒该同步监视器上等待的所有线程。
public static void main(String[] args) throws Exception {
Person p = new Person();
p.start();
synchronized(p){
System.out.println("do Action1");
p.wait();
System.out.println("do Action 4");
}
}
class Person extends Thread{
public void run(){
synchronized(this){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("do Action 2");
this.notify();
System.out.println("do Action 3");
}
}
}
输出结果:
do Action1
do Action 2
do Action 3
do Action 4
线程池
创建一个固定大小(并发大小)的线程池
public class test {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(5);//创建一个固定大小的任务池
for (int i = 0 ;i <9; i++){
service.submit(new Mythread("线程"+ i));
}
service.shutdown();//该方法会等任务池都完成,才会关闭
}
}
class Mythread extends Thread{
String name = "";
Mythread(String name){
this.name = name;
}
public void run(){
System.out.println(this.name);
}
}
还有一些类型的线程池方法,如下:
ExecutorService singleService = Executors.newSingleThreadExecutor();//单线程池
Executors.newCachedThreadPool(); //可变线程池
Executors.newScheduledThreadPool(5);//延迟线程池
Executors.newSingleThreadScheduledExecutor(); //单任务延迟线程池
当某个线程加到线程池中,需要延迟执行的时候
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);//延迟1秒执行