多线程
1.什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
2.多线程的作用?
提高效率
3.多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程比如:软件中的耗时操作、所有的聊天软件、所有的服务器。
并发和并行
1.并发:在同一时刻,有多个指令在单个CPU上交替执行
2.并行:在同一时刻,有多个指令在多个CPU上同时执行
多线程的实现方式
第一种
1.自己定义一个类继承Thread
2.重写run方法
3.创建子类的对象,并启动线程
代码
package a01;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Helloworld");
}
}
}
package a01;
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 =new MyThread();
t1.setName(getName()+"线程1");
t1.start();
}
}
第二种
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
代码
package a02;
public class ThreadDemo {
public static void main(String[] args) {
MyRun mr =new MyRun();
Thread t1 =new Thread(mr);
t1.setName("线程1");
t1.start();
}
}
package a02;
public class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"HelloWorld!");
}
}
}
多线程的优先级
抢占式调度
首先查看t1,t2优先级
代码部分
package a05;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.printf(Thread.currentThread().getName()+"---"+i);
}
}
}
package a05;
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"飞机");
var 坦克 = "坦克";
Thread t2 = new Thread(mr,"坦克");
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
}
}
运行结果
然后将代码设置优先级
package a05;
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"飞机");
var 坦克 = "坦克";
Thread t2 = new Thread(mr,"坦克");
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
System.out.println(Thread.currentThread().getPriority());
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
}
运行结果
设置守护线程
final void setDaemon(boolean on)
细节:
设置为守护线程
当其他的非守护线程执行完毕之后,守抗线程会陆续结束
通俗易懂:
当女神线程结束了,那么备胎也没有存在的必要了
代码部分
package a06;
public class ThreadDemo {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread1 t2 = new MyThread1();
t1.setName("女神");
t2.setName("备胎");
//把第二线程设置为守护线程(备胎线程)
t2.setDaemon(true);
t1.start();
t2.start();
}
}
package a06;
public class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"@"+i);
}
}
}
package a06;
public class MyThread2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"@"+i);
}
}
}
运行结果
线程的生命周期
同步代码块
细节:
1.需要写到循环里面
2.对象需要是唯一的
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数)
特点1:同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定
练习
代码
1.同步代码块
package a07;
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr);
Thread t2=new Thread(mr);
Thread t3=new Thread(mr);
t1.setName("窗口1");
t2.setName("窗12");
t3.setName("窗13");
t1.start();
t2.start();
t3.start();
}
}
package a07;
public class MyRunnable implements Runnable {
int ticket=0;
@Override
public void run() {
//1.循环
while (true){
//2.同步代码块(同步方法)
synchronized (MyRunnable.class){
//3.判断共享数据是否到了末尾,如果到了末尾
if (ticket ==100){
break;
}else {
//4.判断共享数据是否到了末尾
ticket++;
System.out.println(Thread.currentThread().getName()+"在卖第"+ ticket +"张票!!!");
}
}
}
}
}
Lock锁
代码
package a08;
import a07.MyRunnable;
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.setName("窗口1");
t2.setName("窗2");
t3.setName("窗3");
t1.start();
t2.start();
t3.start();
}
}
package a08;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread extends Thread {
static int ticket=0;
static Lock lock = new ReentrantLock();
@Override
public void run() {
//1.循环
while (true){
//2.同步代码块(同步方法)
// synchronized (MyThread.class){
lock.lock();
//3.判断共享数据是否到了末尾,如果到了末尾
try {
if (ticket ==100){
break;
}else {
Thread.sleep(10);
ticket++;
System.out.println(getName()+"在卖第"+ ticket +"张票!!!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
等待唤醒机制(生产者和消费者)
代码
生产者
package a09;
public class Cook extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count == 0){
break;
}else{
if(Desk.foodFlag ==1){
try{
Desk.lock.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}else {
System.out.println("厨师做了一碗面条");
Desk.foodFlag =1;
Desk.lock.notifyAll();
}
}
}
}
}
}
消费者
package a09;
public class Foodie extends Thread {
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count ==0){
break;
}else {
if (Desk.foodFlag == 0){
try {
Desk.lock.wait();
} catch (InterruptedException e){
e.printStackTrace();
}
}else {
Desk.count--;
System.out.println("吃货在吃面条,还能再吃"+ Desk.count +"碗!!!");
Desk.lock.notifyAll();
Desk.foodFlag=0;
}
}
}
}
}
}
其他
package a09;
public class ThreadDemo {
public static void main(String[] args) {
Cook c =new Cook();
Foodie f =new Foodie();
c.setName("厨师");
f.setName("吃货");
c.start();
f.start();
}
}
package a09;
public class ThreadDemo {
public static void main(String[] args) {
Cook c =new Cook();
Foodie f =new Foodie();
c.setName("厨师");
f.setName("吃货");
c.start();
f.start();
}
}
阻塞队列方式实现
代码
import a10.Cook;
import a10.Foodie;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>( 1);
Cook c =new Cook(queue);
Foodie f =new Foodie (queue);
c.start();
f.start();
}
}
package a10;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread {
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue){
this.queue=queue;
}
@Override
public void run() {
try {
String food = queue.take();
System.out.println(food);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package a10;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue){
this.queue=queue;
}
@Override
public void run() {
while (true){
try {
queue.put("面条");
System.out.println("厨师放了一碗面条");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
线程状态
多线程面试题
1.进程和线程的区别是什么?
进程是执行着的应用程序,而线程是进程内部的的一个执行序列。一个进程可以多个线程,线程有叫做轻量级线程。
2.创建线程有几种不同的方式?你喜欢哪一种?为什么?
1.继承Thread类
2.实现Runnable接口
3.应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类,在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口,同时,线程池也是非常高效的,很容易实现和使用。
3.概括的解释下线程的几种可用状态
线程在执行过程中,可以处于下面几种状态:
就绪(Runnable):线程准备运行,不一定能立马就开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞状态,等待外部的处理结束。
睡眠中(Sleeping): 线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchrionization):等待获取锁。
死亡(Dead):线程完成了执行。
4.同步方法和同步代码块的区别是什么?
同步方法默认用this或者当前类class对象作为锁;
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;
同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰;
5.在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码块。
6.什么是死锁?
两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。
7.如何确保n个线程可以访问n个资源的同时又不导致死锁?
使用多线程的时候,指定获取锁的顺序,并强制下爱你出按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。