Java多线程
目录
前言
总结Java多线程的知识
一、Java线程的创建方式
1.继承Thread类
写一个类来继承Thread类,重写run方法。调用这个类实例的start方法会开启一个线程,来执行run方法。
public class Demo1 {
public static void main(String[] args) {
Thread thread = new T1();
thread.start();
}
class T1 extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(i);
}
}
}
2.实现Runnable接口
- 写一个类A实现Runnable接口,重写其run方法
- 写一个类B继承Thread类,将类A的对象传入类B的构造方法中,创建B类的对象
- 调用B对象的start方法,实际执行的是类A的run方法
public class Demo1 {
public static void main(String[] args) {
T2 t2 = new T2();
Thread thread = new Thread(t2);
thread.start();
}
class T2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
for(int i=0;i<100;i++){
System.out.println(i);
}
}
}
3.实现Callable接口
- 写一个类A实现Callable接口,重写call方法
- 将类A的对象传入B的构造方法,new一个FutureTask对象B
- 将B传入Thread类的构造方法new一个Thread类的对象C
- 调用C的start方法,开启一个线程执行call方法(call方法有返回值)
public class Demo1 {
public static void main(String[] args) {
T3 t3 = new T3();
FutureTask<Integer> task = new FutureTask<>(t3);
Thread thread = new Thread(task);
thread.start();
try {
int sum = task.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class T3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i=0;i<10;i++){
sum += i;
}
return sum;
}
}
二、线程中断
调用interrupt方法使线程产生一个InterruptedException异常,进入catch块来进行处理。
public class Demo1 {
public static void main(String[] args) {
Thread thread = new T1();
thread.start();
for(int i=0;i<5;i++){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
thread.interrupt();
}
}
class T1 extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println("线程自杀");
return;
}
System.out.println(i);
}
}
}
三、线程同步
1、synchronized
①、同步代码块
synchronized(object)对括号中的对象加了一把锁,当线程持有这把锁的时候才能进入代码块中;某线程持有这把锁之后,其他线程不得获取这把锁。
//线程同步synchronized
public class Demo8 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1 同步代码块
//格式:synchronized(锁对象){
//
//
// }
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//Object o = new Object(); //这里不是同一把锁,所以锁不住
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
}
}
}
}
}
②、同步方法
synchronized修饰的方法是同步方法。
- 实例方法:锁的是调用此方法的对象。
- 静态方法:锁的是这个类的class对象。
//线程同步synchronized
public class Demo9 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案2 同步方法
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
return true;
}
return false;
}
}
}
2、显式锁
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步代码块和同步方法都属于隐式锁
//线程同步lock
public class Demo10 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1 显示锁 Lock 子类 ReentrantLock
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
//参数为true表示公平锁 默认是false 不是公平锁
private Lock l = new ReentrantLock(true);
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
四、公平锁与非公平锁
- 公平锁:先来先到,排队在前的线程先获得锁
- 非公平锁:争抢,所以等待的线程一起争抢锁
五、线程死锁
//警察say方法中的c.fun()执行要获取罪犯的对象锁
//罪犯say方法中的p.fun()执行要获取警察的对象锁
//相互等待,不释放锁,造成死锁
public class Demo11 {
public static void main(String[] args) {
//线程死锁
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
static class Culprit{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放了人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯被放了,罪犯也放了人质");
}
}
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人质,我放了你");
c.fun();
}
public synchronized void fun(){
System.out.println("警察救了人质,但是罪犯跑了");
}
}
}
六、生产者与消费者
wait()是Object的方法,this.wait()的作用是用this来让当前线程休眠,此线程处于this的监视之下;
this.notifyAll()的作用是使得处于this监控下的线程都被唤醒。
下面这个例子中,两个线程在做完自己的事情后唤醒对方,自己休眠。
public class Demo12 {
public static void main(String[] args) {
//多线程通信 生产者与消费者问题
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
f.setNameAndTaste("老干妈小米粥","香辣味");
}else {
f.setNameAndTaste("煎饼果子","甜辣味");
}
}
}
}
//服务员
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
//true表示可以生产
//boolean flag = true;
public synchronized void setNameAndTaste(String name,String taste){
//if(flag){
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
// flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//}
}
public synchronized void get(){
//if(!flag){
System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
//flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//}
}
}
}
七、线程的状态
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得cpu 时间片后变为运行中状态(running)。 - 阻塞(BLOCKED):表线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间内自行返回。
- 终止(Terminal)
八、线程池
1.缓存线程池
获取方法:Executors.newCachedThreadPool()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo9 {
public static void main(String[] args) {
//线程池
//创建线程
//创建任务
//执行任务
//关闭线程
//缓存线程池
//无限制长度
//任务加入后的执行流程
//1判断线程池是否存在空闲线程 2存在则使用 3不存在则创建线程并使用
//向线程池中加入新的任务
ExecutorService service = Executors.newCachedThreadPool();
//指挥线程池执行新的任务
Runnable task = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
};
service.execute(task);
service.execute(task);
service.execute(task);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(task);
}
}
执行结果
2.定长线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo9 {
/*定长线程池
长度是指定的线程池
加入任务后的执行流程
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程
**/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(task);
service.execute(task);
service.execute(task);
}
}
执行结果
3.单线程线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo9 {
/*单线程线程池
执行流程
1 判断线程池的那个线程是否空闲
2 空闲则使用
3 不空闲则等待它空闲后再使用
**/
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
};
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(task);
service.execute(task);
service.execute(task);
}
}
4.周期定长线程池
package thread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo16 {
/*周期任务 定长线程池
执行流程
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程
周期性任务执行时
定时执行 当某个任务触发时 自动执行某任务
**/
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//定时执行一次
//参数1:定时执行的任务
//参数2:时长数字
//参数3:2的时间单位 Timeunit的常量指定
/* scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
},5, TimeUnit.SECONDS); //5秒钟后执行*/
/*
周期性执行任务
参数1:任务
参数2:延迟时长数字(第一次在执行上面时间以后)
参数3:周期时长数字(没隔多久执行一次)
参数4:时长数字的单位
* **/
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
},5,1,TimeUnit.SECONDS);
}
}