线程的第一种创建方式:继承Thread并重写run方法,在run方法中定义需要并发执行的任务代码。
这种创建线程方式的优点是:结构简单,便于匿名内部类形式创建。
缺点:
1:由于java是单继承的,这导致如果继承了Thread就无法再继承其他类去复用方法,实际开发中会造成很多不便。
2:定义线程时重写了run方法直接将任务定义在了线程中,导致线程与任务存在必然的耦合关系,不利于线程的重用。public class ThreadDemo1 { public static void main(String[] args) { Thread t1 = new MyThread1(); t1.start(); } } class MyThread1 extends Thread{ public void run(){ for(int i=0;i<1000;i++){ System.out.println("你是谁啊?"); } } }
第二种创建线程的方式:实现Runnable接口单独定义线程任务。
@FunctionalInterface函数式编程
public class ThreadDemo2 { public static void main(String[] args) { //实例化线程要执行的任务 Runnable r1 = new MyRunnable1(); //创建线程来执行对应的任务 Thread t1 = new Thread(r1); t1.start(); } } class MyRunnable1 implements Runnable{ public void run(){ for(int i=0;i<1000;i++){ System.out.println("hello!姐~"); } } }
Thread常用APIjava中所有的代码都是靠线程运行的,main方法也不例外。JVM启动后会创建一个线程来执行,main方法,并且该线程的名字也叫"main",因此我们通常称它为"主线程"。但是它和我们创建的线程并无区别。
static Thread.currentThread()//Thread提供了一个静态方法可以获取运行这个方法的线程getPriority();//获取线程优先级
线程的优先级:
线程有10个优先级,分别对应整数1-10 其中1为最低优先级,10为最高优先级。5为默认值
,线程start方法调用后便纳入到了线程调度器中统一管理,此时线程只能被动被分配时间片来
并发运行。线程不能主动索取时间片。调度器会尽可能均匀的将时间片分配给每一个线程。
setPriority(1)修改线程的优先级可以增加该线程获取时间片的概率增加。static sleep(long ms)//线程静态方法,线程调用sleep后就会进入阻塞状态指定的毫秒。超时后线程会自动回到RUNNABLE状态,再次开始并发运行,线程会释放CPU,阻塞时间到了,线程会重新回到就绪队列
sleep方法要求必须处理中断异常。当一个线程调用sleep方法处于睡眠阻塞的过程中,此时该线程的interrupt()方法被调用时,会立即中断该睡眠阻塞,并抛出中断异常。
public class ThreadInfoDemo { public static void main(String[] args) { //让主线程执行currentThread以便得到它 Thread main = Thread.currentThread();//获取到主线程 dosome();//主线程执行dosome方法 String name = main.getName();//获取线程的名字 System.out.println("名字:"+name); long id = main.getId();//获取线程的唯一标识 System.out.println("id:"+id); int priority = main.getPriority();//获取线程优先级 System.out.println("priority:"+priority); boolean isAlive = main.isAlive();//线程是否还活着 boolean isDaemon = main.isDaemon();//线程是否为守护线程 boolean isInterrupted = main.isInterrupted();//线程是否被中断了 System.out.println("是否活着:"+isAlive); System.out.println("是否为守护线程:"+isDaemon); System.out.println("是否被中断:"+isInterrupted); Thread.sleep(1000); } public static void dosome(){ Thread t = Thread.currentThread();//获取运行dosome方法的线程 System.out.println("运行dosome方法的线程:"+t); } }
join()可以协调线程的同步运行
多线程是并发运行,本身是一种异步运行的状态。
同步运行:多个线程执行是存在了先后顺序。
异步运行:各自执行各自的当A线程中调用B.join()后,A线程便进入了阻塞状态。直到B线程执行完毕才会解除A阻塞。
B.join(5000);A线程最多阻塞等待5秒public class JoinDemo { static boolean isFinish = false;//表示图片是否下载完毕 public static void main(String[] args) { Thread download = new Thread(){ public void run(){ System.out.println("down:开始下载图片..."); for(int i=1;i<=100;i++){ System.out.println("down:"+i+"%"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("down:图片下载完毕!"); isFinish = true; } }; Thread show = new Thread(){ public void run(){ System.out.println("show:开始显示文字"); try { Thread.sleep(2000); } catch (InterruptedException e) { } System.out.println("show:显示文字完毕!"); //将show线程阻塞,直到download执行完毕(图片下载完毕) // Thread.sleep(?);//无法准确预估download结束时间 try { download.join(); } catch (InterruptedException e) { } System.out.println("show:开始显示图片..."); if(!isFinish){ throw new RuntimeException("图片加载失败!"); } System.out.println("show:图片显示完毕"); } }; download.start(); show.start(); } }
守护线程java将线程认为两类:用户线程和守护线程。也称为前台线程和后台线程。
守护线程是通过普通的用户线程通过调用setDaemon(true)设置而转变而来的,必须在该线程调用start()之前设置。
因此创建和使用上与用户线程并无区别。但是主要区别在于:
进程的结束:当一个java进程中所有的用户线程都结束时,进程就会结束,此时会将所有的守护线程全部杀死。
线程的生命周期
多线程并发安全问题



