文章目录
一.多线程的实现方式
1.继承Thread类
//1.继承Thread类
class MyThread extends Thread{
//2.重写run方法
public void run() {
System.out.println("继承Thread类开启多线程");
}
}
//3.创建自定义的线程类对象
MyThread mt=new MyThread();
//4.调用start()方法,jvm会开启线程执行run()方法
mt.start();
//简化
new Thread(){
public void run() {
System.out.println("使用匿名类简化---继承Thread类");
}
}.start();
2.实现Runnable接口
//1.实现Runnable接口
class MyRunable implements Runnable{
//2.重写run()方法
public void run() {
System.out.println("实现Runnable接口开启多线程");
}
}
//3.创建Thread对象,并把Runnable实现类传递进构造参数
MyRunable mr=new MyRunable();
Thread t=new Thread(mr);
//4.调用start()方法,jvm会开启线程执行run()方法
t.start();
//简化
new Thread(new Runnable() {
public void run() {
System.out.println("使用匿名类简化---实现Runnable接口");
}
}).start();
3.两种方式区别
-
继承Thread类可以使用Thread类的方法,代码简便,但java只支持单继承,所以如果类已经有父类就不能再继承Thread类
-
实现Runnable接口可以解决父类问题,但代码稍复杂,且不能直接调用Thread类方法
二.线程信息
1.线程名称
//通过构造方法设置线程名称
new Thread("线程一"){
public void run() {
//使用父类方法设置线程名称
super.setName("修改线程名");
//获取线程名称
System.out.println(this.getName());
}
}.start();
2.当前线程对象
new Thread(new Runnable() {
public void run() {
//使用Thread类静态方法currentThread()获取当前线程对象
System.out.println(Thread.currentThread().getName());
}
}).start();
三.线程状态
1.睡眠等待sleep
线程睡眠:当前线程等待指定时间后继续执行线程
try {
//当前线程睡眠1000毫秒后再继续执行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
2.守护线程|后台线程daemon
守护线程:当其他非守护线程全部执行完毕后,守护线程会马上终止执行
Thread t= new Thread(){
public void run(){
try {
Thread.sleep(3000);
//程序不会执行到这里,因为该线程被设置为守护线程,当其他非守护线程执行完毕,该线程会立刻被终止
System.out.println(this.getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//设置t为守护线程,setDaemon()必须先start()前调用
t.setDaemon(true);
t.start();
//判断t线程是否为守护线程
System.out.println(t.isDaemon());
System.out.println("程序结束了");
//输出结果:
// true
// 程序结束了
//main主线程终止了,不存在其他非守护线程,所以守护线程t立刻被终止
3.阻塞线程join
阻塞线程:子线程阻塞主线程,使主线程暂停执行,等子线程执行一段时间或执行完毕后再恢复执行主线程
public static void main(String[] args) throws InterruptedException {
//t线程在main线程中执行,所以main相当于主线程,t线程相当于子线程
Thread t=new Thread(){
public void run() {
for(int i=0;i<1000;i++){
System.out.println(getName()+"...."+i);
}
}
};
t.start();
//阻塞main主线程暂停执行,等待子线程t执行10毫秒后,主线程和子线程再同时继续执行
//当参数为0或不填写时,会等待子线程执行完毕后才会恢复主线程的执行
t.join(10);
//大概在中间输出这句话,如果没有t.join(10),该行内容会输出在第一行
System.out.println("==============程序结束=================");
}
4.线程优先级
线程优先级:优先级越高的线程CPU会优先执行
class MyThread extends Thread{
public void run() {
for(int i=0;i<1000;i++){
System.out.println(getName()+"...."+i);
}
}
}
Thread t1=new MyThread();
Thread t2=new MyThread();
//设置线程优先级,范围是MIN_PRIORITY~MAX_PRIORITY,即1~10
//优先级越高的线程,CPU会优先执行
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
//获取线程的优先级
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
5.礼让线程yield
礼让线程:暂停当前线程,尽可能让其他线程优先执行,该操作也可能无效
四.线程同步
当多个线程同时读写一个数据时可能会导致数据不一致,所以需要一个同步区域解决这个问题。同步区域在同一个时间只能被一个线程访问,当该线程访问完毕,其他线程才能访问同步区域。
同步锁:相同同步锁(对象引用地址一样,与对象属性值是否改变无关)的同步区域会相互排斥
1.同步代码块
//局部内部类访问外部变量需要使用final修饰
final Object obj=new Object();
new Thread(){
public void run() {
//同步锁,锁对象为obj
synchronized (obj) {
try {
//延时3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程一");
}
};
}.start();
new Thread(){
public void run() {
//同步锁,锁对象为obj
synchronized (obj) {
System.out.println("线程二");
}
};
}.start();
//输出结果:
// (等待3秒)
// 线程一
// 线程二
//两个线程的同步代码块都是同样的锁对象,所以这2个方法同时只能被一个线程访问
//因为线程一先执行,所以线程二需要等待线程一的同步代码块执行完毕,才能执行线程二的同步代码块
2.同步方法
- 同步成员方法同步锁对象是本类实例this
- 静态方法同步锁对象是字节码对象
class MyClass{
//1.同步成员方法同步锁对象是本类实例this
public synchronized void print1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(1);
}
public synchronized void print2(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(2);
}
//2.静态方法同步锁对象是字节码对象MyClass.class
public synchronized static void print3(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(3);
}
public synchronized static void print4(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(4);
}
3.死锁
同步代码相互嵌套时,使用相同的锁对象,可能会出现死锁
final Object obj1=new Object();
final Object obj2=new Object();
new Thread("线程一"){
public void run() {
synchronized (obj1) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
}
}
};
}.start();
new Thread("线程二"){
public void run() {
synchronized (obj2) {
synchronized (obj1) {
}
}
};
}.start();
//死锁分析:
//线程一进入obj1同步块,等待3秒
//在线程一等待3秒期间线程二进入obj2同步块
//线程二准备obj1同步块,但发现被线程一占用,所以等待
//线程一等待3秒结束,准备进入obj2同步块,但发现被线程二占用,所以等待
//线程一和线程二相互等待,程序无法向下执行,形成了死锁
五.线程通信
线程通信:当前线程唤醒其他线程的等待状态
1.Object方式
public class Test1 {
public static void main(String[] args) {
final MyClass mc = new MyClass();
new Thread("线程一") {
public void run() {
try {
//延时100毫秒,为了让所有线程都开启成功后再执行print1方法
Thread.sleep(100);
mc.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread("线程二") {
public void run() {
try {
mc.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread("线程三") {
public void run() {
try {
mc.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread("线程四") {
public void run() {
try {
mc.print4();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
//输出结果:
// 我是方法2
// 我是方法3
// 我是方法4
// 1
// 2
// 4
// 3
//分析:
// 1.线程一延时了100毫秒,所以线程二丶三丶四会先输出语句
// 2.线程二丶三丶四都执行了wait()进入被唤醒状态
// 3.线程一开始输出1
// 4.线程一调用notify()随机唤醒线程二丶三丶四中的一个线程
// 5.本次运行结果唤醒的是线程二,所以继续执行wait()后面的代码,输出2
// 6.线程二最后调用了notifyAll(),所以唤醒了所有为被唤醒的线程(线程三丶四)
// 7.线程三丶四分别执行后面的代码,输出4,输出3
}
}
class MyClass {
/*
* 执行流程概述:
* 1.print1()执行完随机唤醒一个线程
* 2.其他方法都会先wait()等待,当被唤醒后会执行notifyAll()把其他线程都唤醒
* */
public synchronized void print1() throws InterruptedException {
System.out.println(1);
//Object方法1:notify()
//随机唤醒一个未被唤醒的线程
this.notify();
}
public synchronized void print2() throws InterruptedException {
System.out.println("我是方法2");
//Object方法2:wait()
//wait()线程立刻等待,到这行代码停止执行,并释放锁
this.wait();
//当被唤醒后执行wait()后面的代码,不是重新执行print2()方法
System.out.println(2);
//Object方法3:notifyAll()
//唤醒其他所有在未被唤醒的线程
this.notifyAll();
}
public synchronized void print3() throws InterruptedException {
System.out.println("我是方法3");
this.wait();
System.out.println(3);
this.notifyAll();
}
public synchronized void print4() throws InterruptedException {
System.out.println("我是方法4");
this.wait();
System.out.println(4);
this.notifyAll();
}
}
2.ReentrantLock与Condition
public class Test3 {
public static void main(String[] args) {
final MyClass2 m = new MyClass2();
new Thread() {
public void run() {
for (int i = 0; i < 3; i++) {
try {
m.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 3; i++) {
try {
m.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 3; i++) {
try {
m.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
//输出结果:
// 1
// 2
// 3
// 1
// 2
// 3
// 1
// 2
// 3
}
}
class MyClass2 {
private int flag = 1;
//1.创建ReentrantLock对象
private ReentrantLock r = new ReentrantLock();
//2.从同一个锁中获取多个条件,不同条件用于区分不同线程,执行各自线程的等待和唤醒
private Condition c1 = r.newCondition();
private Condition c2 = r.newCondition();
private Condition c3 = r.newCondition();
public void print1() throws InterruptedException {
//3.获取锁
r.lock();
// 使用try-finally保证锁能被释放
try {
if (flag != 1) {
//4.条件1的线程等待
c1.await();
}
System.out.println(1);
flag = 2;
//5.唤醒条件2的线程
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放锁
r.unlock();
}
}
public void print2() throws InterruptedException {
r.lock();
try {
if (flag != 2) {
c2.await();
}
System.out.println(2);
flag = 3;
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
r.unlock();
}
}
public void print3() throws InterruptedException {
r.lock();
try {
if (flag != 3) {
c3.await();
}
System.out.println(3);
flag = 1;
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
r.unlock();
}
}
}
六.线程组
线程组:把线程进行分组,方便批量操作
//1.创建线程组
ThreadGroup tgA=new ThreadGroup("A组");
ThreadGroup tgB=new ThreadGroup("B组");
//2.在创建线程时指定所属线程组
Thread t1=new Thread(tgA,"线程一"){
public void run() {
System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
}
};
Thread t2=new Thread(tgA,"线程二"){
public void run() {
System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
}
};
class MyRunnable implements Runnable{
public void run() {
//获取当前线程对象
Thread t=Thread.currentThread();
//3.根据线程对象获取所属线程组对象
ThreadGroup tg=t.getThreadGroup();
//4.获取线程组名
String groupName=tg.getName();
System.out.println(groupName+"----"+ t.getName());
}
}
//5.在创建线程时指定所属线程组
Thread t3=new Thread(tgB,new MyRunnable(),"线程三");
Thread t4=new Thread(tgB,new MyRunnable(),"线程四");
t1.start();
t2.start();
t3.start();
t4.start();
//6.获取线程组最高优先级
System.out.println(tgA.getMaxPriority());
//7.判断此线程组是否守护线程组
System.out.println(tgB.isDaemon());
七.线程池
线程池是存放多个线程的容器,当需要使用线程时直接从线程池中获取,当线程执行完毕不会被销毁而是放回线程池中,这样避免了大量重复创建销毁线程的消耗,提高性能
1.Runnable
Runnable r1=new Runnable(){
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-a");
}
};
Runnable r2=new Runnable(){
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-b");
}
};
Runnable r3=new Runnable(){
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-c");
}
};
//1.创建容量为2的线程池
ExecutorService es= Executors.newFixedThreadPool(2);
//2.提交三个任务
es.submit(r1);
es.submit(r2);
es.submit(r3);
//3.结束线程池
es.shutdown();
//输出结果:
// (等待一秒)
// pool-1-thread-1-a
// pool-1-thread-2-b
// (等待一秒)
// pool-1-thread-1-c
//分析:
// 1.线程池中只有2个线程,所以同时只能开启线程,执行2个任务
// 2.线程a和b会先执行,线程c需要等待
// 3.线程a和b执行完毕后,把两个线程放回线程池
// 4.线程c从线程池中获取线程执行
2.Callable
//1.Callable相对于Runnable线程任务可以有返回值
Callable<Integer> c1=new Callable<Integer>() {
public Integer call() throws Exception {
Integer sum=0;
for(int i=0;i<100;i++){
sum+=i;
}
return sum;
}
};
Callable<String> c2=new Callable<String>() {
public String call() throws Exception {
Thread.sleep(3000);
return "abc";
}
};
//2.创建单个线程的线程池
ExecutorService es= Executors.newSingleThreadExecutor();
//3.提交Callable任务有返回值
Future<Integer> f1= es.submit(c1);
Future<String> f2=es.submit(c2);
Thread.sleep(1000);
//4.判断任务是否已完成
if(f1.isDone()){
//5.获取任务的返回值
System.out.println("任务一返回结果:"+f1.get());
}
//6.尝试取消任务,需要在任务执行完成之前取消,否则会返回false
f2.cancel(true);
//7.判断任务是否被取消
if(f2.isCancelled()){
System.out.println("任务二已取消");
}
System.out.println(f2.isCancelled());
//结束线程池
es.shutdown();
八.定时器Timer
//1.定时器任务
TimerTask tt=new TimerTask() {
public void run() {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
};
//设置日期
Calendar c=Calendar.getInstance();
c.set(2018,7,5,13,24,15);
Date d=c.getTime();
//2.定时器
Timer t=new Timer();
//3.在指定日期定时执行任务
// 第二个参数是首次执行任务的时间
// 如果当前时间小于首次时间,则等待达到了首次执行开始执行任务
// 如果当前时间大于首次时间,会马上就执行任务
// 第三个参数是任务重复执行的间隔,单位毫秒,不填写任务只执行一次
t.schedule(tt, d);
Thread.sleep(1000*30);
//4.取消定时器
t.cancel();