赞
踩
在现代操作在运行一个程序时,会为其创建一个进程。例如启动一个QQ程序,操作系统就会为其创建一个进程。而操作系统中调度的最小单位元是线程,也叫轻量级进程,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。因此我们可以这样理解:
进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。
线程: 是进程中的单个顺序控制流,是一条执行路径 一个进程如果只有一条执行路径,则称为单线程程序。 一个进程如果有多条执行路径,则称为多线程程序。
java真的可以开启线程吗?
开不了,本地方法 底层的 C++ , Java 无法直接操作硬件,因为它是运行在虚拟机之上的
注意:
线程开启不一定立即执行,由cpu调度执行
创建多线程有三种方法,一种是继承Thread类重写run方法,另一种是实现Runnable接口重写run方法。还有一种是实现Callable接口,下面我们分别给出代码示例,
继承Thread类重写run方法:
package com.test;
/**
1. @decrition 继承Thread实现线程
*/
public class ThreadByEx extends Thread{
/**
* 重写run方法
*/
@Override
public void run() {
System.out.println("I'm a thread that extends Thread!");
}
}
实现Runnable接口重写run方法:
package com.test;
/**
2. @decrition 实现Runnable接口重写run方法
*/
public class ThreadByRunnable implements Runnable{
/**
* 实现run方法
*/
@Override
public void run() {
System.out.println("I'm a thread that implements Runnable !");
}
}
实现 Callable 接口
怎么启动线程?
package com.test;
public class MainTest {
public static void main(String[] args) {
//继承Thread启动的方法
ThreadByEx t1=new ThreadByEx();
t1.start();//启动线程
//实现Runnable启动线程的方法
ThreadByRunnable r = new ThreadByRunnable();
Thread t2 =new Thread(r);
t2.start();//启动线程
}
}
运行结果:
I'm a thread that extends Thread!
I'm a thread that implements Runnable !
代码相当简单,不过多解释。这里有点需要注意的是调用start()方法后并不是是立即的执行多线程的代码,而是使该线程变为可运行态,什么时候运行多线程代码是由操作系统决定的。
实现静态代理对比 Thread
new Thread( ()-> System.out.println(“11111”) ).start() ;
new WeddingCompany(new You())).HappyMarry() ;
静态代理模式总结:
真实对象和代理对象都要实现同一接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
lambda 表达式
为什么要用 lambda表达式?
(params) -> expression [表达式]
(params) -> statement [语句]
(params) -> {statements}
a-> System.out.println("i like lambda ---->" +a);
函数式接口
理解 Functional Interface(函数式接口) 是学习 Java8 lambda表达式的关键所在
函数式接口的定义:
public interface Runnable{
public abstract void run();
}
总结:
lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹;
前提是接口为函数式接口
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号。
我们先来看看中断线程是什么?(该解释来自java核心技术一书,我对其进行稍微简化), 当线程的run()方法执行方法体中的最后一条语句后,并经由执行return语句返回时,或者出现在方法中没有捕获的异常时线程将终止。在java早期版本中有一个stop方法,其他线程可以调用它终止线程,但是这个方法现在已经被弃用了,因为这个方法会造成一些线程不安全的问题。我们可以把中断理解为一个标识位的属性,它表示一个运行中的线程是否被其他线程进行了中断操作,而中断就好比其他线程对该线程打可个招呼,其他线程通过调用该线程的 interrupt方法对其进行中断操作,当一个线程调用interrupt方法时,线程的中断状态(标识位)将被置位(改变),这是每个线程都具有的boolean标志,每个线程都应该不时的检查这个标志,来判断线程是否被中断。而要判断线程是否被中断,我们可以使用如下代码
Thread.currentThread().isInterrupted()
while(!Thread.currentThread().isInterrupted()){
do something
}
但是如果此时线程处于阻塞状态(sleep或者wait),就无法检查中断状态,此时会抛出 InterruptedException异常。如果每次迭代之后都调用sleep方法(或者其他可中断的方法),isInterrupted检测就没必要也没用处了,如果在中断状态被置位时调用sleep方法,它不会休眠反而会清除这一休眠状态并抛出InterruptedException。所以如果在循环中调用sleep,不要去检测中断状态,只需捕获InterruptedException。代码范例如下:
public void run(){
while(more work to do ){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
//thread was interrupted during sleep
e.printStackTrace();
}finally{
//clean up , if required
}
}
同时还有点要注意的就是我们在捉中断异常时尽量按如下形式处理,不要留空白什么都不处理!
不妥的处理方式:
void myTask(){
...
try{
sleep(50)
}catch(InterruptedException e){
...
}
}
正确的处理方式:
void myTask()throw InterruptedException{
sleep(50)
}
或者
void myTask(){
...
try{
sleep(50)
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}
}
最后关于中断线程,我们这里给出中断线程的一些主要方法:
void interrupt():向线程发送中断请求,线程的中断状态将会被设置为true,如果当前线程被一个sleep调用阻塞,那么将会抛出interrupedException异常。
static boolean interrupted():测试当前线程(当前正在执行命令的这个线程)是否被中断。注意这是个静态方法,调用这个方法会产生一个副作用那就是它会将当前线程的中断状态重置为false。
boolean isInterrupted():判断线程是否被中断,这个方法的调用不会产生副作用即不改变线程的当前中断状态。
static Thread currentThread() : 返回代表当前执行线程的Thread对象。
首先我们可以通过t.setDaemon(true)的方法将线程转化为守护线程。而守护线程的唯一作用就是为其他线程提供服务。计时线程就是一个典型的例子,它定时地发送“计时器滴答”信号告诉其他线程去执行某项任务。当只剩下守护线程时,虚拟机就退出了,因为如果只剩下守护线程,程序就没有必要执行了。另外JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。最后还有一点需要特别注意的是在java虚拟机退出时Daemon线程中的finally代码块并不一定会执行哦
代码示例:
package com.test; /** * @decrition 守护线程代码示例 */ public class Demon { public static void main(String[] args) { Thread deamon = new Thread(new DaemonRunner(),"DaemonRunner"); //设置为守护线程 deamon.setDaemon(true); deamon.start();//启动线程 } static class DaemonRunner implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("这里的代码在java虚拟机退出时并不一定会执行哦!"); } } } }
因此在构建Daemon线程时,不能依靠finally代码块中的内容来确保执行关闭或清理资源的逻辑。
public class TestDaemon{ God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true); //默认是 false 表示用户线程,正常的线程都是用户线程.... thread.start(); // 上帝守护线程启动 new Thread(you).start(); // 你用户线程启动 } class God implements Runnable{ @Override public void run(){ while(true){ System.out.println("上帝保佑着你") } } } class You implements Runnable{ @Override public void run(){ for(int i = 0; i < 36500; i++){ System.out.println("哈哈哈"); } System.out.println("====goodbye word===="); // Hello World } }
在现代操作系统中基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当线程的时间片用完了就会发生线程调度,并等待着下一次分配。线程分配到的时间片多少也决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。在java线程中,通过一个整型的成员变量 Priority来控制线程优先级 ,每一个线程有一个优先级,默认情况下,一个线程继承它父类的优先级。可以用setPriority方法提高或降低任何一个线程优先级。可以将优先级设置在MIN_PRIORITY(在Thread类定义为1)与MAX_PRIORITY(在Thread类定义为10)之间的任何值。线程的默认优先级为NORM_PRIORITY(在Thread类定义为5)。 尽量不要依赖优先级,如果确实要用,应该避免初学者常犯的一个错误。如果有几个高优先级的线程没有进入非活动状态,低优先级线程可能永远也不能执行。每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中进行选择,尽管这样会使低优先级的线程可能永远不会被执行到。因此我们在设置优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高的优先级,而偏重计算(需要较多CPU时间或者运算)的线程则设置较低的优先级,这样才能确保处理器不会被长久独占。当然还有要注意就是在不同的JVM以及操作系统上线程的规划存在差异,有些操作系统甚至会忽略对线程优先级的设定,如mac os系统或者Ubuntu系统…
getPriority().setPriority(int xxx)
注意:优先级的设定建议在start()调度前,优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度
public class TestPriority{ public static void main(String[] args){ // 主线程优先级 System.out.println(Thread.currentThread().getName+"--->"+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority ); Thread t2 = new Thread(myPriority ); Thread t3 = new Thread(myPriority ); Thread t4 = new Thread(myPriority ); Thread t5 = new Thread(myPriority ); Thread t6 = new Thread(myPriority ); // 先设置优先级,再启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); // MAX_PRIORITY = 10 t4.start(); t5.setPriority(8); t5.start(); t6.setPriority(7); t6.start(); } } class MyPriority implements Runnable{ public void run(){ System.out.println(Thread.currentThread().getName+"--->"+Thread.currentThread().getPriority()); } }
等待阻塞(WAITING):运行的线程执行wait()方法,JVM会把该线程放入等待池中。
同步阻塞(Blocked):运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
超时阻塞(TIME_WAITING):运行的线程执行sleep(long)或join(long)方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
图中的方法解析如下:
Thread.sleep():在指定时间内让当前正在执行的线程暂停执行,但不会释放"锁标志"。不推荐使用。
Thread.sleep(long):使当前线程进入阻塞状态,在指定时间内不会执行。
Object.wait()和 Object.wait(long) :在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的"锁标志",从而使别的线程有机会抢占该锁。 当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。 唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常, waite()和notify()必须在synchronized函数或synchronized中进行调用。如果在non-synchronized函数或non-synchronized中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
Object.notifyAll():则从对象等待池中唤醒所有等待等待线程
Object.notify():则从对象等待池中唤醒其中一个线程。
Thread.yield():方法 暂停当前正在执行的线程对象,yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,yield()只能使同优先级或更高优先级的线程有执行的机会。
Thread.Join():等待该线程终止。把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
boolean isAlive() :测试线程是否处于活动状态
setPriority(int newPriority): 更改线程的优先级
public class TestStop implements Runable{ private boolean flag = true; public void run(){ int i = 0; while (flag){ Systeml.out.println("run.....Thread"+i++); } } public void stop(){ this.flag = false; } public static void main(String[] args){ TestStop testStop = new TestStop(); new Thread(testStop).start(); for(int i=0;i<1000;i++){ System.out.pringln("main"+i); if(i==900){ // 调用 stop 方法 切换标志位,让线程停止 testStop.stop(); System.out.println("线程该停止了"); } } } }
每一个对象都有一个锁,sleep不会释放锁;
public class TestYield{ public static void main(String[] args){ MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ public void run(){ System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); // 礼让 System.out.println(Thread.currentThread().getName()+"线程停止执行"); } }
public class TestJoin implements Runnable{ public void run(){ for(int i=0;i<1000;i++){ System.out.println("线程vip来了"+i); } } public static void mian(String[] args) throws InterruptedException{ TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for(int i=0;i<500;i++){ if(i==200){ thread.join(); //插队 } System.out.println("main" +i); } } }
Thread.State
线程状态。线程可以处于以下状态之一:
public class TestState{ public static void main(String[] args){ Thread thread = new Thread(()->{ for(int i=0;i<5;i++){ try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } System.out.println("//"); }); // 观察状态 Thread.State state = thread.getState(); System.out.println(state); //New // 观察启动后 thread.start(); // 启动线程 state = thread.getState(); System.out.println(state); // Run while(state != Thread.State.TERMINATED){ //只要线程不终止,就一直输出状态 Thread.sleep(100); state = thread.getState(); // 更新线程状态 System.out.println(state); // 输出状态 } } }
来自不同的类
wait => Object
sleep => Thread
关于锁的释放
wait 会释放锁, sleep 睡觉了,抱着锁睡觉,不会释放。
使用范围不同
wait 必须在同步代码块中,sleep可以在任何地方睡
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。