#博学谷IT学习技术支持#
0.什么是多线程?
比如你边打游戏边和女朋友电话,你的大脑cpu 就是开了两个线程,最后会怎么样,大脑过载,游戏输了,女朋友跑了。
1.那什么是并发?什么是并行呢?
并发:你边打游戏,边和你女朋友打电话,这个行为呢就是并发,因为只有一个大脑,这两件事肯定是交替进行的,所以你女朋友会生气。
并行:你打游戏,我来陪你女朋友打电话,那么我和你的行为就是并行,因为有两个大脑,这两件事不会冲突。
2.什么是进程?什么是线程?
你开个游戏呢就是一个进程,在游戏里走位,放技能,就是不同的线程
3.Java中实现多线程有三种实现方式。三种!三种!三种!
3.1 继承Thread类
线程开启是 start() 方法 重写的方法类是 run()
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"我是一个大帅哥!");
}
}
public static void main(String[] args) {
//继承了线程类之后呢,该类呢也就变为一个线程类,你每new一个对象那么就是一个线程
MyThread thread = new MyThread();
MyThread thread1 = new MyThread();
//线程启动,启动了不能重复启动,除非该线程停止之后
thread.start();
thread1.start();
//thread.run()和thread.start()的区别就是线程名字不一样。
//Thread.currentThread().getName()
}
}
3.2第二种是实现Runnable接口
public class MyRunable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"我是一个无敌大帅哥!");
}
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();
//这边需要说明的是Thread自己本身也实现了Runnable接口
Thread mythread = new Thread(myRunable);
mythread.start();
}
}
3.3 Callable<Object>
//Callable<String>这边的String是返回值的类型
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i <10 ; i++) {
System.out.println("我疯狂的E");
}
return "面对疾风吧!";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//任务的容器
MyCallable myCallable = new MyCallable();
//接收返回值的容器
FutureTask<String> futureTask = new FutureTask<>(myCallable);
//线程
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
3.4 线程的一些属性
Thread thread1 = new Thread(futureTask1);
thread1.setName("yasuo");
//线程的优先级越高,那么先执行的概率越高,并不是百分之百抢占
thread1.setPriority(3);
//线程有两种调度,分时调度和抢占调度。java里面是抢占调度
//设置成守护进程,如果别的线程执行完了,那么这个线程也就结束
thread1.setDaemon(true);
4.经典窗口售票问题
4.1 正确示例 --Runnable 实现
public class Ticket12307 implements Runnable{
private int num = 100;
private Integer a = 0;
@Override
public void run() {
//这边的循环就是当作一个不停的程序
while(true){
//这边必须是对象 int 不行
//这边需要注意的是代码块这这边的a必须是同一个对象,我多个线程不能个拿各的a,可以看下面调用
//实际上操作的是一个对象,这个a可以没有实际的意义,可以理解为谁抢到了a的使用权,就可以执行下面的
//代码块的代码。
//思考一下这边a改成this行不行?
synchronized (a){
if(num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
System.out.println(Thread.currentThread().getName()+"还剩"+num);
}else{
System.out.println("没票了!");
break;
}
}
}
}
public static void main(String[] args) {
//这边操作的是一个对象
//用三个线程去调用了
//下面我会举一个不同的例子就是自己用自己的锁
//需要注意的是这便是用Runnable实现的下面用Thread来实现
Ticket12307 ticket12307 = new Ticket12307();
Thread one = new Thread(ticket12307);
Thread two = new Thread(ticket12307);
Thread three = new Thread(ticket12307);
one.setName("窗口1");
two.setName("窗口2");
three.setName("窗口3");
one.start();
two.start();
three.start();
}
}
4.2 错误示例 --Thread实现
public class Ticket12308 extends Thread{
//这边关注一下,为什么加static 因为多线程关系,会创建多个类;
//如果不创建static 那么num就会有多个,操作的不是同一个对象就没有意义了
private static int num = 100;
private static Integer b = 100;
@Override
public void run() {
while(true){
//这边的this 指的就是Ticket12308这个对象,new多个很显然不是一个对象
//这边修改的话必须使用同一个对象
//synchronized (b) 这个是正确的
synchronized (this){//这个是错误的。思考一下synchronized (Ticket12308.class)行不行
if(num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
System.out.println(Thread.currentThread().getName()+"还剩"+num);
}else{
System.out.println("没票了!");
break;
}
}
}
}
public static void main(String[] args) {
//注意这边是new了两个对象那么关注一下上面同步代码块里的this,你觉得两个线程是同一个this吗?
Ticket12308 ticket12308 = new Ticket12308();
Ticket12308 ticket12308Two = new Ticket12308();
ticket12308.start();
ticket12308Two.start();
}
}
4.3 同步方法实现
public class Ticket12309 implements Runnable{
private static int num = 100;
@Override
public void run() {
while(true){
if(SellTickets()){
break;
}
}
}
//这边是用同步代码块,这个也相当于synchronized(this),是在Runnable中实现是可以的,Thread是不行的
private synchronized boolean SellTickets(){
if(num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
System.out.println(Thread.currentThread().getName()+"还剩"+num);
return false;
}else{
System.out.println("没票了!");
return true;
}
}
public static void main(String[] args) {
Ticket12309 ticket12309 = new Ticket12309();
Thread one = new Thread(ticket12309);
Thread two = new Thread(ticket12309);
Thread three = new Thread(ticket12309);
one.setName("窗口1");
two.setName("窗口2");
three.setName("窗口3");
one.start();
two.start();
three.start();
}
}
4.4 lock锁实现--ReentrantLock
import java.util.concurrent.locks.ReentrantLock;
public class Ticket12305 implements Runnable{
private int num = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
//开启锁
lock.lock();
try {
if(num>0){
Thread.sleep(100);
num--;
System.out.println(Thread.currentThread().getName()+"还剩"+num);
}else{
System.out.println("没票了!");
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
}
public static void main(String[] args) {
Ticket12305 ticket12305 = new Ticket12305();
Thread one = new Thread(ticket12305);
Thread two = new Thread(ticket12305);
Thread three = new Thread(ticket12305);
one.setName("窗口1");
two.setName("窗口2");
three.setName("窗口3");
one.start();
two.start();
three.start();
}
}
5.写个死锁(面试题)
//锁嵌套产生的死锁
public class MyDeadLock {
public static void main(String[] args) {
Integer a = 10;
Integer b = 20;
new Thread(()->{
//这边的while 模拟一直有请求
while(true){
synchronized (a){
synchronized (b){
System.out.println("我出来了!!!!问僧特");
}
}
}
}).start();
new Thread(()->{
//这边的while 模拟一直有请求
while (true){
synchronized (b){
synchronized (a){
System.out.println("我出来了!!!!德莱文");
}
}
}
}).start();
}
}
6.老八吃汉堡(线程等待和线程唤醒机制)
重要的不是代码本身,重要的是想一下不加锁,会产生什么情况
这边只有两个线程,唤醒就是唤醒对方
public class OldEightEatHanBaoBao {
//做八个,吃八个
static int num = 8;
//做一个吃一个,hava 表示现在做好了没
static boolean have = false;
static Integer lock = 0;
public static void main(String[] args) {
//老八做汉堡的线程
new Thread(()->{
//这边一直做,做够8个汉堡就结束
while(true){
synchronized (lock){
if(num==0){
//做完了
System.out.println("全部做完了");
break;
}else{
if(!have){
have =true;
System.out.println("老八蜜汁小汉堡!"+num);
num--;
//这边唤醒所有在这把锁等待的线程
//lock.notifyAll();
lock.notify();
}else{
try {
//这边的线程等待状态必须用当前锁对象才可以进行等待
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
//老八吃汉堡
new Thread(()->{
//这边一直吃,全部吃完就停止
while(true){
synchronized (lock){
if(have){
System.out.println("吃完了害嗨害!");
have =false;
//lock.notifyAll();
lock.notify();
}else{
if(num==0){
System.out.println("全部吃完了");
break;
}else{
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
}
}
7.阻塞队列
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Quene {
public static void main(String[] args) throws InterruptedException {
//这边阻塞队列就是用锁做的,取不到就会一直等待
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(5);
//LinkedBlockingQueue queueOne = new LinkedBlockingQueue();
queue.put("1");
System.out.println(queue.take());
queue.take();//如果去不到就会等待。
}
}
8.线程的状态(6种)
线程状态标识 | 线程状态 | 出现时间点 |
NEW | 新建状态 | new Threa() 还没有start的时候 |
RUNNABLE | 就绪状态 | start() 之后cpu调度到了就是运行态,没调度到就是就绪态 |
BLOCKED | 阻塞状态 | 遇到了锁,有其他线程先拿到了锁,等锁释放的过程就是阻塞状态 |
WAITING | 等待状态 | wait() 方法调用之后的状态 |
TIMED_WAITING | 有时间的等待状态 | sleep() |
TERMINATED | 死亡状态 | 线程内定义的方法也就是run()里的方法执行完成之后 |