多线程
并发:交替执行
并行:同时进行
小贴士:command + shift + f7 高亮所有相同变量
public class DemoMultiThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run"+i);
}
}
}
public static void main(String[] args) {
DemoMultiThread mt = new DemoMultiThread();
mt.start();
for (int i = 0; i < 20; i++) {
System.out.println("main" + i);
}
}
执行原理
- main方法压栈执行
- run方法是单线程,start方法会开辟一个新的栈空间
- 多个线程之间互不影响,在不同的栈空间
Thread
获取线程的名称
- Thread类中的getName()
- 可以获取当前正在执行的线程,使用线程中getName()获取线程名称
public class DemoGetName extends Thread{
@Override
public void run() {
String thread_name = getName();
System.out.println(thread_name);
}
}
private static void show02() {
DemoGetName mt = new DemoGetName();
mt.start(); //第一个线程
new DemoGetName().start(); //第二个线程
}
Thread t = Thread.currentThread();
System.out.println(t);
String name = t.getName();
System.out.println(name);
设置线程的名称
- setName(名字)
- 构造一个无参和有参方法,把线程名字传递给父类,让父类给子线程起名字
sleep方法
for (int i = 0; i < 60; i++) {
System.out.println(i);
Thread.sleep(1000);
}
静态方法,传递的是一个毫秒值
创建多线程方式二
实现Runnable接口的类,该类然后实现run方法。Thread构造方法中可以传递该类,start开启线程
-
创建Runable接口的实现类
public class DemoRunable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("i"); } } }
-
在实现类中重写run方法
-
创建runable接口的实现类对象
-
创建thread类对象,传递runable接口的实现类对象
-
调用thread中的start方法
-
private static void show04() { DemoRunable run = new DemoRunable(); new Thread(run).start(); }
#### 继承Thread类和Runnable接口区别
- Runnable接口创建多线程的好处
1. 避免了单继承的局限性
2. 增强了程序的扩展性,降低了耦合性
#### 匿名内部类创建多线程
private static void show05() {
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("hello"+i);
}
}
}).start();
}
#### 同步代码块
syncronized(锁对象){
可能会出现线程安全问题的代码
}
注意
1. 锁对象可以是任意的对象
2. 多线程的锁对象是同一个
private static void show06() {
DemoTicket ticket = new DemoTicket();
Thread mt1 = new Thread(ticket);
Thread mt2 = new Thread(ticket);
Thread mt3 = new Thread(ticket);
mt1.start();
mt2.start();
mt3.start();
}
public class DemoTicket implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){
if(ticket>0){
/* 展现bug
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
ticket --;
}else{
break;
}
}
}
}
}
原理:
使用了一个锁对象,这个锁对象叫同步锁,也叫同步监视器
3个线程一起抢夺cpu执行权,如果进程1先抢到,执行run方法,遇到sychronized代码块这时进程1会检查同步代码块是否有锁对象,如果有,就会获取到锁对象,进入到同步中执行。
进程2抢到了cpu执行权,执行run方法,遇到synchronized代码块,会检查同步代码块是否有锁对象,发现没有就会进入阻塞状态,会一直等待进程1归还锁对象。
同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步
#### 同步方法
1. 把访问了共享数据的代码抽取出来,放到一个方法中
2. 在方法上添加synchronized修饰符
修饰符 synchrond 返回值类型 方法名(params){}
public class DemoSellTicket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
}
}
public synchronized void sellTicket(){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
ticket --;
}else{
return;
}
}
}
同步方法也会将方法内部的代码锁住,只让一个线程执行,锁对象就是实现类对象,也就是this
静态同步方法
静态方法 访问 静态变量
public class DemoSellTicket implements Runnable{
private static int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
}
}
public static synchronized void sellTicket(){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
ticket --;
}else{
return;
}
}
}
**this是创建对象之后产生的,静态方法优先于对象,静态方法的锁对象是本类的class属性**
相当于synchronized(RunnableImpl.class){}
#### Lock锁
1. 在成员位置创建一个reentrantlock对象
2. 在可能出现安全问题的代码钱使用Lock接口类中的lock获取锁
3. 释放锁
@Override
public void run() {
while (true){
lock.lock();
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
ticket --;
}else{
lock.unlock();
break;
}
lock.unlock();
}
}
释放锁最好配合finally一起使用
#### 等待唤醒机制
多线程间的一种协作机制,一个线程进行了规定操作后,就进入等待状态wait,等待其他线程执行完他们的指定代码后,再将其唤醒notify,再有多个线程进行等待时,如果需要可以使用notifyall来唤醒所有的等待线程。
注意:被通知的等待线程,不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以需要重新去获取锁,才能在调用wait方法之后的地方恢复执行。
- 如果可以获取锁,线程就从waiting编程runnable
- 否则从wait set出来,进入entry set,线程从waitting变成blocked
1. wait和notify方法必须要由同一个锁对象调用。
2. wait方法和notify方法是属于object类的方法。
3. wait方法和notify方法必须要在同步代码块或者同步函数中使用
public class ProduceNood implements Runnable {
Noodles noodles = new Noodles();
public ProduceNood(Noodles noodles) {
this.noodles = noodles;
}
@Override
public void run() {
synchronized (noodles){
while (true){
System.out.println("start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end");
noodles.notify();
noodles.setStatus(true);
if(noodles.isStatus()){
try {
noodles.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class ConsumerNood implements Runnable {
Noodles noodles = new Noodles();
public ConsumerNood(Noodles noodles) {
this.noodles = noodles;
}
@Override
public void run() {
synchronized (noodles){
while (true){
System.out.println("consuming");
noodles.setStatus(false);
noodles.notify();
if(!noodles.isStatus()){
try {
noodles.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class DemoTest {
public static void main(String[] args) {
Noodles noodles = new Noodles();
noodles.setStatus(false);
ProduceNood mt1 = new ProduceNood(noodles);
ConsumerNood mt2 = new ConsumerNood(noodles);
new Thread(mt1).start();
new Thread(mt2).start();
}
}
#### 线程池
static ExecutorService newFixedThreadPool(int nThreads)
参数 创建线程池中包含线程数量
返回值: executorService接口,返回的是ExecutorService接口的实现类对象,可以用ExecutorService接收(面向接口编程)
用来从线程池中获取线程调用start方法,执行线程任务,submit(runnable task)提交一个runnable任务用于执行,void shutdown() 关闭
步骤
1. 使用线程池工厂类Excutors提供静态方法newFixedThreadPool生产一个指定线程数量的线程池
2. 创建一个类,实现runnable接口,重写run方法,设置线程任务
3. 调用ExecutorService中的submit,传递线程任务(实现类),开启线程,执行run
4. 调用ExecutorService的shutdown销毁线程池(不建议执行)
public class DemoThreadPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了线程池");
}
}
private static void show01() {
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(new DemoThreadPool());
es.submit(new DemoThreadPool());
es.submit(new DemoThreadPool());
}