第一种继承Thread类
package cn.tedu.day01;
/*继承Thread类*/
public class ThreadDemo1 extends Thread{
@Override
public void run() {
for (int i=1;i<=200;i++){
System.out.println(getName()+i);
}
}
}
package cn.tedu.day01;
public class ThreadTest1 {
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start();
for (int i = 0; i < 200; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
第二种实现Runnable接口
package cn.tedu.day01;
/*实现runnable接口*/
public class ThreadDemo2 implements Runnable{
@Override
public void run() {
for (int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
package cn.tedu.day01;
public class ThreadTest2 {
public static void main(String[] args) {
ThreadDemo2 threadDemo2 = new ThreadDemo2();
Thread thread = new Thread(threadDemo2,"新线程");
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
第三种:实现Callable接口
- 创建一个类,实现Callable接口
- 重写Callable接口中的call方法
- 创建Callable接口的实现类对象
- 创建FutureTask创建,把Callable实现类对象传入到FutureTask构造方法中
- 创建Thread对象,把FutureTask对象传入到Thread类的构造方法中
- 开启线程
- 通过FutureTask对象调用get方法,拿到返回值,线程才结束。
package cn.tedu.day01;
import java.util.Random;
import java.util.concurrent.Callable;
/*创建一个类,实现Callable接口(泛型类型自定义)
重写Callable接口中的call方法
创建Callable接口的实现类对象
创建FutureTask创建,把Callable实现类对象传入到FutureTask构造方法中
创建Thread对象,把FutureTask对象传入到Thread类的构造方法中
开启线程
通过FutureTask对象调用get方法,拿到返回值,线程才结束。
* */
public class ThreadDemo3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return new Random().nextInt(10);
}
}
package cn.tedu.day01;
import java.util.concurrent.FutureTask;
public class ThreadTest3 {
public static void main(String[] args) throws Exception{
ThreadDemo3 threadDemo3 = new ThreadDemo3();
FutureTask<Integer> futureTask = new FutureTask<>(threadDemo3);
Thread thread = new Thread(futureTask);
thread.start();
//拿到线程返回值时,线程才结束
Integer integer = futureTask.get();
System.out.println(integer);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
优缺点比较
- 第一种继承Thread类
- 代码编写简单,可以直接使用Thread类中的方法
- java中类属于单继承,已经继承Thread类,无法继承其它类
- 第二种实现Runnable接口
- 避免了单继承的局限性,可以理解为降低耦合性
- 代码比较复杂,如果想要使用Thread中的方法,需要先获取当前线程的对象,再调用方法
- 第三种实现Callable接口
- 有返回值,可以抛出异常,避免单继承的局限性
- 实现起来比较复杂,访问线程中的方法,需要使用thread.currentThread()
线程状态!线程的生命周期!
- 创建线程,进入到了新建状态,调用start()方法,得到cpu调度,进入运行状态,正常执行完进行死亡(销亡)状态;没有得到cpu调度,进行阻塞状态。
- 运行状态调用sleep(long)进入休眠状态,执行时间到,再进行cpu执行权判断。
- 运行状态调用Object.wait()进行无限永久等待状态,Object.notify()唤醒,再进行cpu执行权判断。
- sleep方法在同步代码块或者同步方法中不释放锁的
-
package cn.tedu.day01; import java.text.SimpleDateFormat; import java.util.Date; public class ThreadTest4 { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 20; i++) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String format = sdf.format(date); System.out.println(format); Thread.sleep(1000); } } }
- wait方法在同步代码块或者同步方法中释放锁
- join方法用线程对象调用,半路杀出个程咬金(代码案例)
-
package cn.tedu.day01; /* * 匿名内部类 创建类的子类对象或者接口的实现类对象!! * */ public class ThreadDemo4 { public static void main(String[] args) { final Thread t1=new Thread("线程一"){ @Override public void run() { for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); } } }; t1.start(); new Thread("线程二"){ @Override public void run() { for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); if (i==5){ try { t1.join(); } catch (InterruptedException e) { } } } } }.start(); } }
yied :让出cpu执行权 ,具备抢夺cpu执行权能力!
线程的同步(同步锁)
- 在多线程并发中,有多段代码同时执行,我们需要在某一段代码在执行过程中,cpu不要切换到其它线程上,就需要用到同步
- 匿名内部类,使用本方法的局部变量,必须final修饰
- 同步代码块;synchronized(任意的唯一锁对象)
- 同步方法:只需要在方法上加上synchronized即可(非静态方法锁得是this,静态方法锁得是当前类的字节码对象)
- 线程同步:优点:多线程共享数据,,,可以保证数据的安全! 缺点:效率低,可能会出现死锁
package cn.tedu.day01;
public class ThreadTest5 {
public static void main(String[] args) {
final ThreadDemo6 threadDemo6 = new ThreadDemo6();
new Thread("A"){
@Override
public void run() {
while (true){
threadDemo6.print1();
}
}
}.start();
new Thread("B"){
@Override
public void run() {
while (true){
threadDemo6.print2();
}
}
}.start();
}
}
class ThreadDemo6{
public synchronized void print1(){
System.out.print("李");
System.out.print("文");
System.out.print("斌");
System.out.println("斌");
}
public synchronized void print2(){
System.out.print("徐");
System.out.print("晴");
System.out.print("晴");
System.out.println("晴");
}
}
线程的通信(可以交叉进行)
多个线程并发执行时,默认情况下cpu是随机进行切换线程,如果希望多个进程之间可以规律执行,这时候就必须加以控制,用到线程的通信!!!
线程等待:wait()
唤醒等待的线程:notify()
注意:这两个方法必须使用在同步当中,并且使用同步锁进行调用
package cn.tedu.day01;
public class ThreadTest5 {
public static void main(String[] args) {
final ThreadDemo6 threadDemo6 = new ThreadDemo6();
new Thread("A"){
@Override
public void run() {
while (true){
try {
threadDemo6.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread("B"){
@Override
public void run() {
while (true){
try {
threadDemo6.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class ThreadDemo6{
int flag=1;
public synchronized void print1() throws InterruptedException {
if (flag!=1){
this.wait();
}
System.out.print("李");
System.out.print("文");
System.out.print("斌");
System.out.println("斌");
flag=2;
this.notify();
}
public synchronized void print2() throws InterruptedException {
if (flag!=2){
this.wait();
}
System.out.print("徐");
System.out.print("晴");
System.out.print("晴");
System.out.println("晴");
flag=1;
this.notify();
}
}
线程当中如何抛出异常
callable可以抛出异常,其它可以捕获,或者抛出自定义异常
多线程如何避免死锁(同步代码 进行嵌套 )
线程池的工作原理
为什么使用线程池?
如果并发的线程数量很多,并且每一个线程执行一个很短的任务就结束了,这样频繁的创建线程会大大降低系统的效率;因为频繁创建和销毁线程需要时间,线程池相当于一个容器,里面可以容纳很多的线程,池子中的线程可以反复的进行使用,省去了频繁创建和销毁的过程,从而提高效率
优点:
- 降低资源消耗
- 提高响应速度(当使用线程时,不需要等待创建,直接从线程池进行获取使用)
- 提高线程的可管理性
Jdk5之前,需要我们自己手动创建线程池
集合进行管理,add(new Thread(...)),remove传索引,返回的是删除元素的对象,可以使用,list.add进行归还,LinkedList.removeFirst()返回对象,即为线程对象,addFirst归还线程
jdk5之后,jdk内置了线程池
Executor并不是一个线程池,而只是一个执行线程的工具,而真正的线程池是ExecutorService其实现类ThreadPoolExector
线程池用到的是阻塞队列
入队:非阻塞队列,当入队元素超过容器长度,数据丢失;阻塞队列,当入队元素超过容器长度,进行等待,出队完成后在进行存储。
出队:非阻塞队列,容器没有元素,直接得到null值,阻塞队列,容器中没元素,等待有入队之后,再进行出队。
-
一个任务,已存在的一个核心线程,可以解决
-
多个任务,等待的任务!!没有超过阻塞队列长度,存放在阻塞队列中,等待核心线程依次执行!
-
多个任务,等待的任务超过了阻塞队列的最大长度,就会开始创建新的线程,如果新建线程数+核心线程数>最大线程数,直接报错;如果没有超过,那就正常创建,核心线程增加,分摊任务!!