应用程序:一个可运行的程序,例如:QQ,微信等
一个应用程序至少有一个进程
-进程:应用程序中的一个执行单元
-线程:是进程中的一个执行单元(最底层的工人)
注意:每个线程都是互相独立的,各自干自己的活,互相不影响,如果同一个进程中有多个线程,那么这些线程共享这个进程里面的所有资源
线程的生命周期:
-新建(new)
-就绪(runnable)
-运行(running)
-阻塞(yield,sleep,wait,join等)
-死亡(dead)
如下图所示:
线程的工作原理:
线程通过创建之后,经过strat()方法启动之后,就不再受我们的控制了,会自己执行任务,而当我们启动了线程之后,线程不会立即进入到工作状态,而是处于就绪(Runnable),线程真正的运行是必须通过cpu的线程调度来完成的,(也就是必须等待cpu分配时间片),在这段时间片之内,线程并不一定就能完成任务,当时间片结束之后,线程又会回到就绪状态,等待下一次cpu分配时间片,并抢到时间片,才能继续工作,一旦工作完成,就会进入死亡状态,等待回收.
java中线程的创建
方法一:继承Thread类
class MyThread1 extends Thread{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊");
}
}
}
方法二:实现Runnable
class MyThread3 implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("我是送外卖的");
}
}
}
注意:第二种方式实现Runnable接口之后,并没有start()方法来启动线程,因为该接口中只定义了一个run方法,只是单纯的用来定义线程执行任务的,所以想要启动线程,还得创建一个Thread对象,来将这个任务对象传入.
方法三:匿名内部类创建(也是我们最常用的方法)
Thread t4 = new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("我是买水喝的");
}
}
};
线程中一些常用的方法
getName:获取当前线程的名字
Thread.currentThread():获取执行当前任务/方法的线程名字`
getPriority():获取当前线程的优先级
setPriority():设置当前线程的优先级
-优先级分1-10级,默认是5
-理论上来讲,优先级越高的线程,抢到时间片的概率越大,但是,实际情况 还是得看线程能否抢得到
-isDaemon():判断线程是一个守护线程(例如:gc)
-setDaemon(true):将当前线程设置为守护线程
-守护线程:后台线程,当程序中没有一个前台线程在运行了,那么后台线程会自动的结束掉
-Thread.sleep(long time):让线程休眠一段时间
注意:Thread.sleep(1000)线程休眠,不会立即进入到运行状态,而是进入就绪状态,分配到时间片,抢到之后,才重新运行
Thread.yield():阻塞
注意:该方法会立即让线程进入就绪状态
join():谁的里面出现join,就阻塞谁,谁点出来join,就等谁先执行完
A线程{
B线程.join();//等它先执行完
}
wait():阻塞(等待)
-wait方法是Object里面的
-notify():唤醒下一个排队的线程
-notifyAll:同时唤醒所有在排队的线程
守护线程之Jack和Rose和FuckMan的故事
public static void main(String[] args) {
Thread rose = new Thread(){
public void run(){
for(int i=0;i<10;i++){
System.out.println("rose:Let me go!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("音效:啊啊啊啊噗通~");
}
};
Thread jack = new Thread(){
public void run(){
while(true){
System.out.println("jack:You jump!I jump!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Thread fuckMan = new Thread(){
public void run(){
while(true){
System.out.println("fuckMan:Son of bitch!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
jack.setDaemon(true);//将杰克设置为守护线程
fuckMan.setDaemon(true);//也设置为守护线程
rose.start();
jack.start();
fuckMan.start();
}
}
阻塞线程join的用法:谁里面出现join就阻塞谁,谁点出来join就等谁先执行完
/**
* join:谁里面出现join就阻塞谁,谁点出来join就等谁先执行完
*
*/
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t1 =new Thread(){
public void run(){
System.out.println("正在下电影...");
for(int i=0;i<=100;i++){
System.out.println("已下载"+i+"%");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("下载完毕!");
}
};
Thread t2 = new Thread(){
public void run(){
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始观看电影...");
System.out.println("已看完");
}
};
t1.start();
t2.start();
}
}
线程的并发问题
线程在异步执行的时候有可能会引发并发问题,这时候就需要认为的加锁来让线程按照同步的方式执行.
synchronized:加锁
* 谁先访问的该方法,那么就给这个方法加一个锁,别人就只能
* 在外面等,等到前面的线程执行完出来之后,才会进入到该方法
public synchronized int getBean(){
if(bean==0){
throw new RuntimeException("已经没有豆子了");
}
bean--;
System.out.println(bean);
Thread.yield();
return bean;
}
还有同步块,代码用同步块包起来的话就能保证同一时间只有一个线程能调用这一个代码块
synchronized(this){}
synchronized(this){
System.out.println(name+"正在试衣服...");
Thread.sleep(3000);
}
线程池:用来管理线程
因为当我们手动不停的创建线程和销毁线程,是会造成资源损耗的,如果情况严重的话,是会直接导致系统崩溃的,所以为了避免这种情况,我们可以利用线程池,自动的维护一些可重复利用的线程,来解决这个问题
public static void main(String[] args) {
//创建一个线程池(里面最多同时存在3个线程)
ExecutorService es = Executors.newFixedThreadPool(3);
//创建5个任务
for(int i=0;i<6;i++){
Runnable rb = new Runnable(){
public void run() {
String name=Thread.currentThread().getName();
System.out.println(name+"正在执行任务...");
try {
Thread.sleep(3000);
System.out.println(name+"执行完毕");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
System.out.println("指派了第"+(i+1)+"个任务");
//将指派的任务交给线程池处理
es.execute(rb);
/**
* 关闭线程池
* 注意:shutdown()不会强制结束已经运行的线程,如果线程已经
* 派出去执行任务了,还是会等到该线程执行完毕之后,再回首
* shutdownNow():无论如何都会立即停止所有的线程
*/
// es.shutdown();
es.shutdownNow();
System.out.println("线程池已关闭!");
}
}
新建线程池:ExcutorService es = Excutors.newFixedThreadPool(3);3是指线程池里面有三个线程
启动线程池:es.excute(rb);
要解决的任务写在Runnable rb = new Runnable(){
public void run(){
}
} ;
这个匿名内部类中
关闭线程池:es.shutdown();
es.shitdoenNow();