Java 多线程总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 多线程总结相关的知识,希望对你有一定的参考价值。
1.简介
在现实的生活中,有很多的事情是可以同时进行的,比如一边听歌,一边撸着代码。在java中,为了模拟现实中的这种情况因此引入了线程的机制。
进程:在操作系统中的多任务执行时以进程为单位的,系统分配进程有限的cup时间片去执行某进程,下一个cup时间片又去执行其他的进程,
由于cpu转换较快,使得进程好像是在同时执行。
线程:线程是存在于进程中的执行流程,一个进行可以包括多个线程,这些线程共享进程的内存,每个线程可以得到一小段的执行时间,因此一个
进程就能具有多个并发执行的线程。一个进程至少要有一个线程。
2.线程的实现
线程的实现主要有两种方式:
1.继承Thread类,重写run方法,在其中书写线程任务逻辑
2.定义线程要执行的任务,即定义一个类实现Runnable接口,然后创建线程的同时将任务指定
public class CreatThreadDemo01 { public static void main(String[] args) { Thread thread1 = new Dream(); Thread thread2 = new Reality(); /** * 启动线程要调用start方法,不能直接调用run方法 * * start方法会将当前线程纳入到线程调度中,使其具有并发运行能力。 * start方法很快会执行完毕,当start方法执行完毕后,当前线程的run方法会很快的被执行 * 起来(只要获取到了cpu的时间)。但不能理解为调用start方法时run方法就执行了! * * 线程有几个不可控因素: * 1:cpu分配时间片给哪个线程我们说了不算 * 2:时间长短也不可控 * 3:线程调度会尽可能均匀的将时间片分配给多个线程。 * */ thread1.start(); thread2.start(); } } /** * 第一种创建线程的方式存在两个不足: * 1:由于java是单继承的,这就导致我们若继承了Thread类就无法再继承其他类,这在写项目时会遇到很大问题。 * 2:由于我们定义线程的同时重写run方法来定义线程要执行的任务,这就导致线程与任务有一个强耦合关系, * 线程的重要性变得非常局限。 */ class Dream extends Thread{ public void run() { for(int i = 0; i < 1000; i++){ System.out.println("理想很丰满!"); } } } class Reality extends Thread{ public void run(){ for(int i = 0; i < 1000; i++){ System.out.println("现实很骨感。。。。"); } } }
public class CreatThreadDemo02 { public static void main(String[] args) { Runnable r1 = new MyThread1(); Runnable r2 = new MyThread2(); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); } } class MyThread1 implements Runnable { public void run() { for (int i = 0; i < 1000; i++) { System.out.println("干什么"); } } } class MyThread2 implements Runnable{ public void run(){ for (int i = 0; i < 10000; i++) { System.out.println("去吃饭了"); } } }
使用匿名内部类创建线程
public class CreatThreadDemo03 { public static void main(String[] args) { //使用匿名内部类创建线程 //方式1 Thread t1 = new Thread(){ public void run(){ for(int i = 0; i < 1000; i++){ System.out.println("天气很热"); } } }; t1.start(); //方式2 Runnable r = new Runnable() { public void run() { for (int i = 0; i < 1000; i++) { System.out.println("热就开风扇啊"); } } }; Thread t2 = new Thread(r); t2.start(); } }
3.线程的一些操作方法使用
1.获取运行当前方法的线程
public class UseThreadDemo { public static void main(String[] args) { Thread mt = Thread.currentThread(); System.out.println("运行main方法的线程为:"+mt); Thread t = new Thread(){ public void run(){ Thread myT = Thread.currentThread(); System.out.println("自定义的线程为:"+myT); doSome(); } }; t.start(); } public static void doSome(){ Thread t = Thread.currentThread(); //指定线程运行 if(!t.toString().equals("Thread[Thread-0,5,main]")){ return; } System.out.println("运行doSome方法的线程为:"+t); } }
2.获取线程的信息操作
public class ThreadInfoDemo { public static void main(String[] args) { Thread t = Thread.currentThread(); //获取id System.out.println("线程id:"+t.getId()); //获取名字 System.out.println("线程名字:"+t.getName()); //获取优先级 System.out.println("线程的优先级为:"+t.getPriority()); //是否是守护线程 System.out.println("是否是守护线程:"+t.isDaemon()); //是否被中断 System.out.println("是否被中断:"+t.isInterrupted()); } }
3.线程的优先级
每个线程都具有各自的优先级,代表在程序中该线程的重要性,当多个线程处于就绪状态时,优先级高的程序等到cpu时间片的几率就高,
也就是运行的几率会大些。
线程的优先级分10个等级,1-最低,5-默认,10-最高。
MIN_PRIORITY: 1 对应最低优先级
MAX_PRIORITY: 10 对应最高优先级
NORM_PRIORITY: 5 默认优先级
public class ThreadPriorityDemo { public static void main(String[] args) { Thread max = new Thread(){ public void run(){ for (int i = 0; i <1000; i++) { System.out.println("我是优先级最高的max"); } } }; Thread min = new Thread(){ public void run(){ for (int i = 0; i <1000; i++) { System.out.println("我是优先级最低的min"); } } }; Thread norm = new Thread(){ public void run(){ for (int i = 0; i <1000; i++) { System.out.println("我是优先级为默认的norm"); } } }; max.setPriority(10); min.setPriority(Thread.MIN_PRIORITY); //启动 min.start(); norm.start(); max.start(); } }
4.线程的休眠
Thread类提供了静态方法 static void sleep(long ms)用于指定该线程的休眠时间,以毫秒计算。
当超时之后,线程会自动回到Runnable状态,等待再次分配时间片运行。(不代表超时之后立即运行,只能保证进入就绪的状态)
public class ThreadSleepDemo { public static void main(String[] args) { Thread t = new Thread(){ public void run(){ while(true){ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); System.out.println(sdf.format(new Date())); //休眠一秒 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; t.start(); } }
5.守护线程使用
Java中有两类线程,User Thread-用户线程,也叫前台线程、Daemon Thread-守护线程,也称后台线程。
当进程中的所有User Thread结束了,则进程也就结束了,无论进程中的其他Daemon Thread是否正在运行,都会被牵制中断。
public class DaemonThreadDemo { public static void main(String[] args) { Thread t1 = new Thread(){ public void run(){ for (int i = 0; i < 10; i++) { System.out.println("我是User Thread!"); } try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("User Thraed 运行完了"); } }; Thread t2 = new Thread(){ public void run(){ while(true){ System.out.println("我是Daemon Thread"); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; t2.setDaemon(true); t1.start(); t2.start(); System.out.println("main 方法运行完成"); //while(true); // 看看加上之后的效果 } }
6.线程的加入
Thread中join()方法能够允许当前线程在另一个线程上等待,直到该线程结束工作。
通常用来协调两个线程工作时使用。比如Thread A里面插入了线程B,则线程A会等待线程B执行完成之后再继续执行下去。
public class ThreadJoinDemo { public static void main(String[] args) { Thread threadA = new Thread(){ public void run(){ System.out.println("thread A 开始计数:"); for (int i = 0; i < 100; i++) { System.out.println("Thread A:"+i); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("Thread A 计数完毕!"); } }; Thread threadB = new Thread(){ public void run(){ System.out.println("我要等Thread A 计数完成我再开始,好气哦!"); try { //等待threadA执行完毕在向下执行 threadA.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 101; i < 200; i++) { System.out.println("Thread B:"+i); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("Thread B 计数完毕!"); } }; threadA.start(); threadB.start(); } }
7.线程的礼让
Thread 提供了yield()方法,给正在运行的线程提醒,告知它可以将资源礼让给其他线程,但这只是一种暗示,不能保证当前线程一定
会将资源让出来。可以用来模拟线程之间的切换。
4.线程的同步
在多线程的任务中,如果多个线程操作同一个资源时,由于线程切换的不确定性,可能会导致逻辑出现混乱,严重
时可能导致系统崩溃,业务逻辑出现错误等情况。
java中使用synchronized 关键字来确保线程使用资源时能够同步进行。该关键字有两种用法:
1.修饰方法:这样的话,该方法就称为“同步方法”,多个线程就不能同时进入到方法内部去执行,可以避免由于线程切换不确定,导致的逻辑错误。
2.synchronized块:可以将某段代码片段括起来,多个线程不能同时执行里面的代码。
public class SyncDemo { public static void main(String[] args) { final Table t= new Table(); Thread t1 = new Thread(){ public void run(){ while(true){ int bean =t.getBeans(); Thread.yield();//模拟线程切换 System.out.println(getName()+" "+bean); } } }; Thread t2 = new Thread(){ public void run(){ while(true){ int bean=t.getBeans(); Thread.yield();//模拟线程切换 System.out.println(getName()+" "+bean); } } }; t1.start(); t2.start(); } } class Table{ private int beans =20; /** * synchronized 关键字 * 该关键字有两个用法: * 1:修饰方法,这样的话,该方法就称为“同步方法” * 多个线程就不能同时进入到方法内部去执行,可以避免由于线程切换不确定,导致的逻辑错误。 * 2:synchronized块,可以将某段代码片段括起来,多个线程不能同时执行里面的代码。 * @return */ public synchronized int getBeans(){ if(beans==0){ throw new RuntimeException("没有豆子了"); } Thread.yield();//模拟线程切换 return beans--; } }
/** * 使用synchronized 块的意义 * 有效的缩小同步范围,可以在保证安全的前提下提高并发效率。 * */ public class SyncDemo02 { public static void main(String[] args) { final Shop shop = new Shop(); Thread t1 = new Thread(){ public void run(){ shop.buy(); } }; Thread t2 = new Thread(){ public void run(){ shop.buy(); } }; t1.start(); t2.start(); } } class Shop{ public void buy(){ try { Thread t = Thread.currentThread(); System.out.println(t+"正在挑衣服~"); Thread.sleep(5000); /** * synchronized 块 * 若想使用同步块达到同步效果,必须保证多个线程看到的“同步监视器”(上锁的对象) * 是同一个才有效果! * 通常使用this就可以了。 * * synchronized 若修饰的是方法,也是有上锁的对象,该对象就是当前方法所属的对象,也就是this。 */ synchronized (this) { System.out.println(t+"正在试衣服"); Thread.sleep(5000); } System.out.println(t+"结账离开了"); } catch (InterruptedException e) { e.printStackTrace(); } } }
/** * synchronized 也可以达到代码间的互斥 * 两个同步块括起来不同的两段代码,但是只要上锁的对象相同,那么这两段 * 代码间就存在互斥效果。 * @author lenovo * */ public class SyncDemo04 { public static void main(String[] args) { final Foo foo = new Foo(); Thread t1= new Thread(){ public void run(){ foo.methoA(); } }; Thread t2= new Thread(){ public void run(){ foo.methoB(); } }; t1.start(); t2.start(); } } class Foo{ public synchronized void methoA(){ Thread t = Thread.currentThread(); System.out.println(t+"正在调用A方法"); try { t.sleep(5000); } catch (Exception e) { } System.out.println(t+"将方法A执行完毕"); } public synchronized void methoB(){ Thread t = Thread.currentThread(); System.out.println(t+"正在调用B方法"); try { t.sleep(5000); } catch (Exception e) { } System.out.println(t+"将方法B执行完毕"); } }
wait()和notify()方法使用
/** * Object 类中定义了两个方法wait(),notify() * 他们也可以实现协调线程之间同步工作的方法。 * * 当一个线程调用了某个对象的wait方法时,这个线程就进入阻塞状态,直到这个对象的notify方法 * 被调用,这个线程才会解除wait阻塞,继续向下执行代码。 * * 若多个线程在同一对象上调用wait方法进行阻塞状态后,那么当该对象的notify方法被调用时,会随机解除一个线程 * 的wait阻塞,这个不可控。 * 若希望一次性将所有线程的wait阻塞解除,可以调用notifyAll方法。 * @author lenovo */ public class WaitAndNotifyDemo { /** * 局部内部类(方法内的)无调用方法外的局部变量,必须为final * 下面isfinish的被调用,因此写在main方法外,成为成员变量,类的属性、 */ private static boolean isFinish=false; private static Object obj = new Object(); public static void main(String[] args) { final Thread download = new Thread(){ public void run(){ System.out.println("down:开始下载图片"); for(int i=0;i<=100;i++){ System.out.println("down:开始下载:"+i+"%"); try { Thread.sleep(50); } catch (Exception e) { } } System.out.println("down:图片下载完成"); isFinish=true; //通知show线程开始工作 synchronized(this){ this.notifyAll(); } System.out.println("down:开始下载附件:"); for(int i=0;i<=100;i++){ System.out.println("down:"+i+"%"); try { Thread.sleep(50); } catch (Exception e) { } } System.out.println("down:附件下载完毕!"); } }; Thread show = new Thread(){ public void run(){ System.out.println("show:开始显示图片"); try { synchronized(download){ download.wait(); } //download.join(); } catch (Exception e) { } if(!isFinish){ throw new RuntimeException("图片未下载完成"); } System.out.println("show:图片显示完成!"); } }; download.start(); show.start(); } }
5.线程池的创建
线程池: 当我们的逻辑中出现了会频繁创建线程的情况时,就要考虑使用线程池来管理线程。 这可以解决创建过多线程导致的系统威胁。 线程池主要解决两个问题: 1:控制线程数量 2:重用线程
public class ThreadPoolDemo { public static void main(String[] args) { //创建固定大小的线程池 ExecutorService pool = Executors.newFixedThreadPool(2); //指派任务 for (int i = 0; i < 5; i++) { Runnable r = new Runnable() { public void run() { Thread t = Thread.currentThread(); System.out.println("线程"+t+"在执行"); try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("线程"+t+"执行完成"); } }; pool.execute(r); } System.out.println("线程池停止"); pool.shutdown(); } }
以上是关于Java 多线程总结的主要内容,如果未能解决你的问题,请参考以下文章