多线程
进程 :正在内存中运行的程序
线程:线程是进程中的一个执行单元
一个进程中至少有一个线程
cpu一次只能处理一条指令,所谓同时是因为人反应不过来
分为多个时间片段,尽可能平均分配给每一个线程
多线程并不会提高运行时间,它只是分别每次只执行一个任务,在没有执行完就切换执行下一个
线程的创建 : 第1种方法:
1.定义一个类继承(extends)Thread
2.重写run方法
3.创建Thread子类对象,就是创建线程对象
4.调用start方法,开启线程执行run方法
Thread.currentThread()//返回正在被执行的线程信息 .getName() //获取正在被执行的线程信息的名字
缺点:1. Java是单继承,继承thread后就无法继承其他的
2. 当继承Thread 并重写run方法,在run方法中定义要执行的任务。这会导致线程与任务之间存在一个必然的耦合关系,不利于线程的
psvm{
Thread t1 = new MyThread1();//创建子类对象
Thread t2 = new MyThread2();
t1.start();//先调用start方法,而不是run方法。run方法是创建的类中的,这里是让线程跑起来
t2.start();
}
class MyThred1 extends Thread{
public void run(){
要并行执行的语句
}
}
class MyThred2 extends Thread{
public void run(){
要并行执行的语句
}
}
第2种方法:
实现Runnable接口重写run方法
- 定义类实现(implements)Runnable接口。或者实现Callable接口(如果线程执行完毕需要返回值时使用,多用于线程池)
- 实现接口中的run方法
- 创建Runnable接口的子类对象
- 将子类对象作为参数传递给创建的Thread类的构造方法
- 调用Thread类的start方法开启线程
//定义类实现Runnable接口
public class MyRunnable1 implements Runnable{
//实现接口中的run方法
public void run(){
要并行执行的语句
}
}
public class MyRunnable2 implements Runnable{
public void run(){
要并行执行的语句
}
}
public class {
psvm{
//创建线程要执行的任务。创建Runnable接口的子类对象
Runnable r1 = new MyRunnable();//这是向上造型么?
Runnable r2 = new MyRunnable();
//创建线程。将子类对象作为参数传递给Thread的构造方法
//这种写法是将任务和线程分开,Thread可以干更多的事,不局限于一个任务
Thread t1 = new Thread(r1(只要是继承Runnable接口或者是它的对象都可以),"此处可以设置线程的名字");
Thread t2 = new Thread(r2);
//启动。调用Thread类的start()方法开启线程
t1.start();
t2.start();
}
}
线程安全
有多个线程在同时运行,而这些线程同时运行某段代码。程序每次运行的结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的一样,就是线程安全。
如果不一样,就是出现了线程安全隐患。
出现安全隐患的必要条件:
- 多线程
- 同时使用一个资源
- 写操作(添加,删除,修改)
只需要破坏其中一个条件就可以解决线程安全隐患
同步代码块
在代码块声明一个锁(synchronized)
两个线程调用同一对象才有效果,他们看到的是同一对象。()内写的是同步监视对象,必须是一个引用类型
(new Object):对象一定不同,锁无论如何都没有效果
(“abc”):写abc在main中调用多个对象,此时已经不是多线程,但是锁还是发挥作用。不是一个合适的锁对象
(this):this是一个合适的锁对象,因为它在调用多个对象时,会失效。看到的是不同的类。
在静态方法中写括号中的同步监视对象时,括号内不能是this,因为静态中没有this,需要使用类对象,就是类名.Class
```java
锁对象是共有的
synchronized(锁对象){
可能产生线程安全问题的代码
}
public static synchronized void consumer(){}
在同步代码块中,在同一时刻只能有一个线程在执行,加入锁之后相当于给门上锁,我正在里边的代码时不允许其他人访问,我结束执行了才可以。
同步代码块中的锁对象可以是任意对象,但是多个线程时,要使用同一个锁对象才能保证线程安全。否则容易出死锁
同步方法
在普通方法声明上添加synchronized,就是同步方法。
成员方法所属于对象
同步方法中所的对象不能改,只能是this
权限修饰符 synchronized 方法的返回值类型 方法名(){
可能会产生问题的代码;代码执行完之后才会解锁
}
同步方法可以保证在同一时刻方法中只有一条线程
静态同步方法
在 静态方法的声明上添加synchronized,就是静态同步方法
静态方法所属类,全局只有一份。
当一个静态方法使用synchronized后,那么该方法一定具有同步效果。不论new多少个都需要排队做
静态方法上指定的同步监视器对象为当前类的类对象(.Class)
类对象:Class的实例
权限修饰符 static synchronized 方法的返回值类型 方法名(){
可能会产生线程安全问题的代码;
}
同步和异步
同步:一段代码在同一时刻只允许一个线程执行
异步:一段代码在同一时刻允许多个线程执行
同步一定是线程安全的
线程安全的不一定同步
异步不一定线程不安全
线程不安全一定是异步的
ArrayList 是异步线程不安全的
LinkedList是异步线程不安全的
HashMap是异步线程不安全的
Hashtable是同步线程安全的
ConcurrentHashMap 是一个异步线程安全的,采用分桶锁的机制
死锁
由于锁的嵌套导致锁之间相互锁死的现象
synchronized(s){//抢到s线程之后,等待释放p线程
synchronized(p){
}
}//大括号结束释放s线程
synchronized(p){//抢到p线程之后,等待释放s线程
synchronized(s){
}
}//大括号结束释放p线程
等待唤醒机制
类.wait() :让线程进入等待池等待
类.notify():从等待池中唤醒线程
等待唤醒机制必须结合锁来用
使用的锁对象和唤醒的锁对象应该是同一对象
wait和sleep的区别
wait()方法是Object类的方法,在同步代码块或同步方法中调用,如果不指定休眠时间,调用之后会释放对象的锁,使当前线程进入等待状态,直到其他对象的notify()或notifyAll()方法来唤醒它。唤醒它之后代码向下执行
sleep()方法是Thread类的静态方法,可以在任何地方调用,它会使得当前线程暂停执行指定的时间,不会释放任何锁
当一个线程调用sleep方法在睡眠阻塞中,调用interrupt()方法会中断该线程的睡眠阻塞。sleep方法会抛出中断异常
等待唤醒机制
生产者与消费者
wait() 执行后从线程池唤醒一个。
执行完语句后也唤醒一个
若只是p.notify(); 。唤醒两个c,线程都进入等待
外边有两个p,等待池里有两个c沉睡(此时两个c都在p.wait()处,已经判断完毕),p执行结束后为false之后随机唤醒,若唤醒其中一个c,c抢到之后执行完把结果改为true,改为true之后要唤醒一个,若唤醒另一个c,另一个c抢到线程之后不再判断,从唤醒处向下执行,c还是true,还会执行一次c。
线程池的使用
提前创建多个线程,放入一个集合中。需要时取出一个线程为它服务,不需要时再放回
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//向线程池中布置任务
executorService.submit(new 一个类中继承Callable接口的call方法)//submit是执行
executorService.execute(new 一个类中继承Run接口的run方法)。执行线程池
//关闭线程池
executorService.shutdown();
使用线程池执行方法
第四种创建线程的方法
单例设计模式
单例模式可以保证某一个类创建出的对象在整个项目中只有一个
分为懒汉式和饿汉式
饿汉式
将构造方法设置为private,禁止外面的类调用。
在内部设置静态的私有的变量,这样外面可以获取。
设置公开的方法返回静态静态的私有的变量
守护线程
守护线用于守护别的线程。只有所有线程都结束了,那么守护线程也随之结束
如果一个线程不是守护线程,那么就是被守护线程。所有的被守护线程结束,守护线程才随之结束
main是主线程,main方法执行完就会结束,所以不一定是最后结束的线程。如果主线程不结束,进程就不会杀死守护线程
GC(垃圾回收机制)就是一个守护线程。当程序结束后,GC也会结束
守护线程只能在start() 执行前启动
Thread thread = new Thread();
thread.setDaemon(true);//守护线程
thread.start();
设置线程优先级
优先级越高,越有可能执行
优先级分为1-10
Thread.MAX_PRIORITY 为10
Thread.NORM_PRIORYTY 为5
Thread.MIN_PRIORITY 为1
Thread t1 = new Thread();
t1.setPriority(1);//设置线程优先级
线程状态
Thread.State state = thread.getState();
sout(state)
获取线程的状态
NEW 尚未启动的线程
RUNNABLE 正在jvm中执行的线程
BLOCKED 阻塞的线程
WAITING 无限期等待另一个线程要执行某一特定操作的线程
TIMED_WAITING 等待另一线程执行取决于等待时间的操作,否则我等完就要执行
TERMINATED已经结束的线程