多线程
多线程
一、线程简介
1. 概念
程序:指令和数据的有序集合,静态概念。
进程:执行程序的一次执行过程,动态概念。是系统资源分配的单位。
线程:是CPU调度和执行的基本单位。
2. 线程的三种创建方式
(1)继承Thread类
package com.snmwyl.Thread;
//step1:继承Thread类
public class TestThread1 extends Thread {
//step2:重写run()方法
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我在吃饭");
}
}
public static void main(String[] args) { //main线程
//step3:创建一个线程对象,调用start()方法开启线程
TestThread1 t = new TestThread1();
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("我在睡觉");
}
}
}
单继承局限性
(2)实现Runnable接口
package com.snmwyl.Thread;
//step1:实现runnable接口
public class TestThread2 implements Runnable {
//step2:重写run()方法
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我在吃饭");
}
}
public static void main(String[] args) { //main线程
//step3:创建runnable接口的实现类对象
TestThread2 t = new TestThread2();
//step4:创建一个线程对象,调用start()方法开启线程
new Thread(t).start();
for (int i = 0; i < 100; i++) {
System.out.println("我在睡觉");
}
}
}
推荐使用:方便同一个对象被多个线程使用。
例子1:
package com.snmwyl.Thread;
public class TestThread3 implements Runnable {
//多线程抢票
private int ticketNums = 10;
public void run() {
while (true) {
if(ticketNums == 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums--+"张票。");
}
}
public static void main(String[] args) {
TestThread3 t = new TestThread3();
new Thread(t,"张三").start();
new Thread(t,"李四").start();
new Thread(t,"王五").start();
}
}
问题:多个线程访问统一资源,可能造成数据紊乱。
例子2:
package com.snmwyl.Thread;
public class TestThread4 implements Runnable {
//龟兔赛跑
private static String winner;
public void run() {
for (int i = 1; i <= 100; i++) {
boolean flag = gameOver(i);
if(flag) {break;}
System.out.println(Thread.currentThread().getName()+"跑了"+ i + "米了。");
if(Thread.currentThread().getName() == "兔子" && i % 10 == 0) {
//模拟休息
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private boolean gameOver(int steps){
if(winner != null) {
return true;
}
if(steps == 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner+"!!!!!!!!");
return true;
}
return false;
}
public static void main(String[] args) {
TestThread4 t = new TestThread4();
new Thread(t,"兔子").start();
new Thread(t,"乌龟").start();
}
}
(3)实现Callable接口
/*
1. 实现Callable接口,需要返回值类型
2. 重写call方法,需要抛出异常
3. 创建目标对象
4. 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
5. 提交执行
Future<Boolean> result1 = ser.submit(t1);
6. 获取结果
boolean r1 = result1.get();
7. 关闭服务
ser.shutdownNow();
*/
3.静态代理模式
package com.snmwyl.Thread;
/*
静态代理模式:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
*/
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany w = new WeddingCompany(new Person());
w.marry();
}
}
//接口
interface Marry {
void marry();
}
//真实角色
class Person implements Marry {
public void marry() {
System.out.println("我们结婚吧!");
}
}
//代理角色
class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
public void marry() {
before();
target.marry();
after();
}
private void before() {
System.out.println("布置现场。");
}
private void after() {
System.out.println("打扫现场。");
}
4.Lamda表达式
函数式接口:只包含唯一一个抽象方法的接口。
public interface Runnable{
public abstract void run();
}
可以通过lambda表达式来创建函数式接口的对象。
package com.snmwyl.Thread;
public class TestLambda {
//3.静态内部类
static class ILike2 implements Like {
public void lambda() {
System.out.println("I like lambda2.");
}
}
public static void main(String[] args) {
Like l = new ILike1();
l.lambda();
l = new ILike2();
l.lambda();
//4.局部内部类
class ILike3 implements Like {
public void lambda() {
System.out.println("I like lambda3.");
}
}
l = new ILike3();
l.lambda();
//5.匿名内部类(没有类的名称,必须借助接口或者父类)
l = new Like(){
public void lambda() {
System.out.println("I like lambda4.");
}
};
l.lambda();
//6.用Lambda简化
l = () -> {
System.out.println("I like lambda5.");
};
l.lambda();
}
}
//1.定义一个函数式接口
interface Like{
void lambda();
}
//2.外部实现类
class ILike1 implements Like {
public void lambda() {
System.out.println("I like lambda1.");
}
}
二、线程状态
创建状态、就绪状态、运行状态、阻塞状态、死亡状态。
1.线程停止
package com.snmwyl.Thread;
public class TestStop implements Runnable {
//1.设置一个标识为
private boolean flag = true;
public void run() {
int i = 0;
while(flag){
System.out.println("thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop t = new TestStop();
new Thread(t).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i == 900){
t.stop();
System.out.println("stoooooooop");
}
}
}
}
2.线程休眠sleep
package com.snmwyl.Thread;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
1.模拟网络延时,放大问题的发生性
2.模拟倒计时
3.打印当前系统时间
每个对象都有一把锁,sleep不会释放锁
*/
public class TestSleep {
/*//2.模拟倒计时
public static void main(String[] args) {
try {
ten();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//模拟倒计时
public static void ten () throws InterruptedException {
int num = 10;
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0) {
break;
}
}
}*/
//3.打印当前系统时间
public static void main(String[] args) {
Date d = new Date(System.currentTimeMillis());
while (true) {
try{
System.out.println(new SimpleDateFormat("HH:mm:ss").format(d));
Thread.sleep(1000);
d = new Date(System.currentTimeMillis());
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
3.线程礼让yield
暂停但不阻塞,将线程从运行状态转为就绪态,礼让不一定成功。
package com.snmwyl.Thread;
public class TestYield {
public static void main(String[] args) {
MyYield y = new MyYield();
new Thread(y,"A").start();
new Thread(y,"B").start();
}
}
class MyYield implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
4.线程强制执行join
package com.snmwyl.Thread;
public class TestJoin implements Runnable {
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println("线程VIP"+i);
}
}
public static void main(String[] args) {
TestJoin t = new TestJoin();
Thread thread = new Thread(t);
thread.start();
for (int i = 1; i <= 5; i++) {
if(i == 3){
try {
thread.join();//main线程阻塞
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("主线程"+i);
}
}
}
5.观测线程状态
package com.snmwyl.Thread;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread t =new Thread(()->{
for (int i = 0; i < 3; i++) {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("//////");
});
Thread.State state = t.getState();
System.out.println(state); // NEW
t.start();
state = t.getState();
System.out.println(state); // RUNNABLE
while (state != Thread.State.TERMINATED) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
state = t.getState();
System.out.println(state); // TIMED_WAITING ...
// //////
// TERMINATED
// 一旦进入死亡状态就不能再次启动
}
}
}
6.线程的优先级
package com.snmwyl.Thread;
public class TestPriority {
public static void main(String[] args) {
//线程默认优先级 5
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
MyPriority p = new MyPriority();
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(p);
Thread t4 = new Thread(p);
Thread t5 = new Thread(p);
Thread t6 = new Thread(p);
t1.start();
//先设置优先级再启动
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//10
t4.start();
t5.setPriority(8);
t5.start();
t6.setPriority(7);
t6.start();
}
}
class MyPriority implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
}
优先级低只意味着获得调度的概率低。
7. 守护线程daemon
package com.snmwyl.Thread;
//线程分为用户现场和守护线程
//虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕
//守护线程,如后台记录操作日志、监控内存、垃圾回收等待
//上帝守护你
public class TestDeamon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t = new Thread(god);
t.setDaemon(true);//默认false表示用户现场
t.start();
new Thread(you).start();
}
}
//上帝 守护线程
class God implements Runnable {
public void run() {
while (true) {
System.out.println("god bless u");
}
}
}
//你
class You implements Runnable {
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("living");
}
System.out.println("======bye world======");
}
}
四、线程同步
并发:同一个对象被多个线程同时操作。
线程同步形成条件:队列+锁。
1. synchronized
//同步方法
//无需指定Obj,默认为this或class
public synchronized void method(int args){}
//同步块
//Obj:同步监视器,一般是共享资源
synchronized(Obj){}
package com.snmwyl.Thread;
import java.util.concurrent.CopyOnWriteArrayList;
//并发编程包JUC:Java Util Concurrent
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();//读多写少场景适用
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
产生死锁的四个必要条件:
-
互斥条件:一个资源每次只能被一个进程使用。
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:进程以获得的条件,在未使用完之前,不能强行剥夺。
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
2. Lock锁
package com.snmwyl.Thread;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock2 t = new TestLock2();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;
private final ReentrantLock lock = new ReentrantLock();//可重复锁
public void run(){
while(true){
try{
lock.lock();//加锁
if(ticketNums>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(ticketNums--);
}else{
break;
}
}finally{
lock.unlock();//解锁
}
}
}
}
3. synchronized与Lock的对比
- Lock显式锁(手动开启和关闭锁),synchronized隐式锁(出了作用域自动释放)。
- Lock只有代码块锁,synchronized有代码锁和方法锁。
- 使用Lock锁,JVM花费较少的时间来调度线程,性能更好。并且具有更好的拓展性(提供更多的子类)。
优先使用顺序:
Lock > 同步代码块 > 同步方法
五、线程通信问题
生产者消费者问题
1. 管程法
package com.snmwyl.Thread;
//生产者消费者模型
//管程法:利用缓冲区解决
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread {
SynContainer container;
//构造方法
public Producer(SynContainer container){
this.container = container;
}
//生产
public void run(){
for (int i = 0; i < 100; i++) {
container.addFood(new Food(i));
System.out.println("Produce food " + i);
}
}
}
//消费者
class Consumer extends Thread {
SynContainer container;
//构造方法
public Consumer(SynContainer container){
this.container = container;
}
//消费
public void run(){
for (int i = 0; i < 100; i++) {
container.removeFood();
System.out.println("Consume food " + i);
}
}
}
//产品
class Food{
int id;
//构造方法
public Food(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
Food[] foods = new Food[10];//缓冲区大小
int count = 0;//初始化缓冲区
//生产者放入产品
public synchronized void addFood(Food f){
//缓冲区已满
if(count == foods.length){
//通知消费者消费,生产者等待
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
//缓冲区未满
foods[count] = f;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Food removeFood(){
//不能消费
if(count == 0){
//消费者等待
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
//能消费
count--;
Food food = foods[count];
//通知生产者生产
this.notifyAll();
return food;
}
}
2. 信号灯法
package com.snmwyl.Thread;
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者,演员
class Player extends Thread {
TV tv;//节目
public Player(TV tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
tv.play("神兵小将");
}else{
tv.play("妈祖");
}
}
}
}
//消费者,观众
class Watcher extends Thread {
TV tv;//节目
public Watcher(TV tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品,节目
class TV{
String name;//节目名称
boolean flag = true;//真:没有节目;假:有节目
//表演
public synchronized void play(String name){
if(!flag){//有节目
try{
this.wait();//演员等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("演员 is playing " + name);
this.notifyAll();//通知观众观看
this.name=name;
this.flag=!this.flag;
}
//观看
public synchronized void watch(){
if(flag){//没有节目
try{
this.wait();//观众等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("观众 is watching " + name);
this.notifyAll();//通知演员表演
this.flag=!this.flag;
}
}
3. 线程池
好处:
- 提高响应速度(减少了创建新线程的时间)。
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)。
- 便于线程管理。
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
使用线程池:
-
ExecutorService线程池接口,常见子类ThreadPoolExecutor
void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般用来执行Callable void shutdown():关闭连接池
-
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package com.snmwyl.Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);//参数为线程池大小
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭链接
service.shutdown();
}
}
class MyThread implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
浙公网安备 33010602011771号