JAVA多线程基础
1.什么是并发原理?
多线程:多线程允许我们“同时”执行多段代码。
线程是并发运行的,线程调度会统一规划CPU时间,将CPU的时间划分为若干片段,然后尽可能的均匀分配给所要并发运行的线程,每个线程在获取CPU时间片段后CPU就会运行它的任务,当时间片段用完后,Cpu会离开并执行获取到CPu时间片的线程,所以,所有线程并非真正的“同时”运行着代码,而是走走停停,这种微观上走走停停,在宏观上感觉是同步进行的现象称为“并发运行”。
2.创建线程Thread
1. 方式一:继承Thread并重写run方法来定义线程任务;
public class ThreadDemo {
public static void main(String[] args) {
Thread t1=new MyThread1();
Thread t2=new MyThread2();
/*
* 启动线程不是调用Run方法,而是要调用start()方法,
* 线程开始并发运行时会自动调用run()方法。
*/
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
public void run(){
for(int i=0;i<100;i++){
System.out.println("你好,兄弟");
}
}
}
class MyThread2 extends Thread{
public void run(){
for(int i=0;i<100;i++){
System.out.println("我不好!!!!!");
}
}
}
第一种创建线程的方式有两个不足的地方
1.由于java是单继承,这就倒置若继承了Thread就不能在继承其他的类,在实际开发中并不方便,因为无法重用其他类的某些方法。
2.由于继承Thread后重写run()方法定义线程要执行的任务,这就导致了 线程与线程执行任务有一个必然的耦合关系,不利于线程的重用。
2.第二种创建线程的方法:实现Runnable接口单独定义线程任务
public class ThreadDemo2 {
public static void main(String[] args) {
//创建线程任务
MyThread3 m1=new MyThread3();
MyThread4 m2=new MyThread4();
//创建线程并执行任务
Thread t1=new Thread(m1);
Thread t2=new Thread(m2);
//执行任务
t1.start();
t2.start();
}
}
//线程任务
class MyThread3 implements Runnable {
public void run() {
for(int i=0;i<100;i++){
System.out.println("淦淦淦!");
}
}
}
class MyThread4 implements Runnable{
public void run() {
for(int i=0;i<100;i++){
System.out.println("草草草!");
}
}
}
使用Runnable创建并使用线程实现Runnable接口并重写run方法来定义线程体,然后再创建线程的时候将Runnable的实例传入并启动线程,这样做的好处在于可以线程和线程要执行任务分离,降低耦合度。
3.使用匿名内部类实现创建线程的两种方式
public class ThreadDemo3 {
public static void main(String[] args) {
//创建线程
Thread t1=new Thread(){
public void run() {
for(int i=0;i<100;i++){
System.out.println("淦淦淦!");
}
}
};
//创建线程任务
Runnable r=new Runnable() {
public void run() {
for(int i=0;i<100;i++){
System.out.println("草草草!");
}
}
};
Thread t2=new Thread(r);
//启动线程
t1.start();
t2.start();
}
}
3.currentThread();
线程提供了static Thread currentThread(),该方法可以获取运行这个方法的线程,java中所有的代码都是线程运行的,main方法也不例外,jvm启动后会启动一个线程来执行main方法,通常我们成执行main方法的线程为“主线程”.
public class CurrentThreadDemo {
public static void dosome(){
Thread t=Thread.currentThread();
//Thread[main,5,main]
//Thread[线程名称,优先级,线程组]
System.out.println("运行dosome的线程:"+t);
}
public static void main(String[] args) {
dosome();
Thread t2=Thread.currentThread();
System.out.println(t2);
Thread t3=new Thread(){
public void run(){
Thread r=Thread.currentThread();
System.out.println("t3线程"+r);
}
};
//启动
t3.start();
}
}
代码运行结果:
运行dosome的线程:Thread[main,5,main]
Thread[main,5,main]
t3线程Thread[Thread-0,5,main]
4.线程的优先级
一.线程的优先级:线程调用start()方法后纳入线程调度统一管理,线程不能主动获取cpu时间片段,只能被动分配,调正先线程优先级可大程度的改善某个线程获取cpu时间片段的概率,理论上线程优先级越高的线程获取cpu时间片段的次数越多。
二.线程优先级10个等级(1-10)
1.表示优先级最低
5.表示默认
10.表示优先级最高
三.设置优先级方法setPriority(int p);
线程三个常量分别表示最低,最高,默认
public class priorityDemo {
public static void main(String[] args) {
Thread t1=new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("max");
}
}
};
Thread t2=new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("默认");
}
}
};
Thread t3=new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("min");
}
}
};
//设置线程的优先级
t1.setPriority(Thread.MAX_PRIORITY);//10
t2.setPriority(Thread.NORM_PRIORITY);//5
t3.setPriority(Thread.MIN_PRIORITY);//1
//启动
t1.start();
t2.start();
t3.start();
}
}
结果分析:
max先输出完,下来是默认,再是min。
5.睡眠阻塞状态
1.static void sleep(long ms)当一个线程执行完sleep方法后就会进入阻塞状态指定的毫秒数,超时后线程会自动回到runnable就绪状态等待两次并发运行,该方法要求必须处理interruptedException。
2.睡眠阻塞状态时被其他线程调用interruput方法时,会抛出该中断异常并打断睡眠阻塞状态。
3.interrupt()方法用于中断线程,但是若线程处于阻塞状态时中断阻塞,若线程没有处于阻塞状态则线程直接被中断。
1.案例一:用睡眠阻塞模拟小品事件
public class SleepDemo {
public static void main(String[] args) {
Thread qiang=new Thread(){
public void run(){
System.out.println("强哥:刚贴上面膜,睡会。。。。。");
try {
Thread.sleep(10000);
System.out.println("我醒来了!");
} catch (InterruptedException e) {
System.out.println("强哥:卧槽,咋回事!小老弟");
}
}
};
Thread chao=new Thread(){
public void run(){
System.out.println("刘永超:我开始砸墙了");
for(int i=0;i<5;i++){
System.out.println("刘永超:80");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("刘永超:咣当,搞定!!");
qiang.interrupt();
}
};
//启动
chao.start();
qiang.start();
}
}
运行结果:
强哥:刚贴上面膜,睡会。。。。。
刘永超:我开始砸墙了
刘永超:80
刘永超:80
刘永超:80
刘永超:80
刘永超:80
刘永超:咣当,搞定!!
强哥:卧槽,咋回事!小老弟
2.案例二:用睡眠阻塞状态实现图片切换
public class SleepDemo2 {
public static void main(String[] args) {
JFrame rame=new JFrame();//相框
rame.setSize(350,350);//大小
JPanel panel=new JPanel();//面板
panel.setSize(350,350);
rame.setContentPane(panel);//把面板放到相框中
rame.setVisible(true);//设置相框可见
//点击关闭按钮同时关闭程序
rame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Thread t=new Thread(){
public void run(){
int i=0;
while(true){
i=i==0?1:0;
try {
Thread.sleep(500);
if(i==0){
panel.setBackground(Color.BLACK);
}else{
panel.setBackground(Color.green);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
//启动
t.start();
}
}
运行结果:
一个面板中会有两张分别为红色和绿色底色来回切换,切换间隔为500ms。
6.join()方法
线程提供了一个方法void join()该方法允许线程在另一个线程上等待,直到执行完毕后再继续运行,这样做可以协调线程间的“同步运行”。
同步运行:代码执行有先后顺序(单线程下运行时是同步的,多线程下也可以进行同步运行操作)
异步运行:代码各执行各的。(多线程下运行代码时是异步的)
案例:模拟图片下载
public class JoinDemo {
public static void main(String[] args) {
//线程(下载)
Thread download=new Thread(){
public void run(){
System.out.println("开始下载图片。。");
for(int i=10;i<=100;i=i+10){
try {
System.out.println("下载进度:"+i+"%");
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("图片下载完毕!!");
}
};
//线程(显示图片)
Thread show=new Thread(){
public void run(){
try {
download.join();
System.out.println("图片显示完毕");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
//运行
download.start();
show.start();
}
}
运行结果:
开始下载图片。。
下载进度:10%
下载进度:20%
下载进度:30%
下载进度:40%
下载进度:50%
下载进度:60%
下载进度:70%
下载进度:80%
下载进度:90%
下载进度:100%
图片下载完毕!!
图片显示完毕
7.守护线程setDaemon(boolean a)
守护线程(后台线程):
1.守护线程又称为后台线程,默认创建的线程都不是后台线程,守护线程需要调用线程提供的方法void setDaemon( boolean b),当参数为true时,线程变为守护线程。
2.守护线程和普通线程上在使用上没有什么区别,但是结束时有一个区别:
当进程结束时,所有正在运行的守护线程会强制结束,也就是说,也就是说,普通线程结束时,守护线程会强制结束。
案例:模拟泰坦尼克号情景剧
public class DaemonThread {
public static void main(String[] args) {
//线程1(rose)
Thread rose=new Thread(){
public void run(){
for(int i=0;i<10;i++){
System.out.println("rose: 让我去死。");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("rose:扑通。。。");
}
};
//线程2(jack)
Thread jack=new Thread(){
public void run(){
while(true){
System.out.println("jack:你死我也死!!!");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
//给jack设置成守护线程(后台线程)
//设置守护线程在start方法之前方法前调用
jack.setDaemon(true);
rose.start();
jack.start();
}
}
运行结果:
jack:你die我也die!!!
rose: 让我去die。
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
jack:你die我也die!!!
rose:扑通。。。
从上面的运行可以看出当rose不再说话了,jeck也不说话了,说明rose线程结束,jack线程也跟着结束了。
8.查看线程的相关方法
public class InforDemo {
public static void main(String[] args) {
//获取该方法的线程信息
Thread main=Thread.currentThread();
System.out.println(main);
//获取线程id
long id=main.getId();
System.out.println(id);
//获取线程名字
String name=main.getName();
System.out.println(name);
//获取线程的优先级
int p=main.getPriority();
System.out.println(p);
//判断线程是否还活着
boolean b=main.isAlive();
System.out.println(b);
//判断线程是否是后台线程
boolean b1=main.isDaemon();
System.out.println(b1);
//判断线程是否被中断
boolean b2=main.isInterrupted();
System.out.println(b2);
}
}
9.线程池
线程池主要完成工作:
1.管理多线程数量,由于多个每个线程会占用一定的内存,线程数量过多会导致内存占用过大,还有个问题就是cpu过度切换会导致程序(避免浪费资源)出现卡顿。
2.重用线程。
public class ThreadPool {
public static void main(String[] args) {
//创建线程池
ExecutorService pool=Executors.newFixedThreadPool(2);
for(int i=0;i<10;i++){
//10个任务
Runnable runn=new Runnable() {
public void run() {
String name=Thread.currentThread().getName();
System.out.println(name+"正在执行任务。。。");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
//将线程任务指定给线程池
pool.execute(runn);
}
/*
* shutdown()与shutdownNow()都是线程池停止状态
* shutdown()方法调用后,线程池不再接受新任务,会将已有的任务全部执行后再停止
* shutdownNow()调用后,线程池会调用所有线程的中断方法后立即停止
*/
pool.shutdown();
}
}
运行结果:
会有两个线程来一起执行任务,大大降低的运行时间,提高了运行效率。