Day26-多线程
多线程
线程的基本状态
1、初始(新生)状态
2、就绪状态
3、运行状态
4、终止(死亡)状态
常见方法
休眠sleep
/**
* sleep:
* 休眠
* 使线程停止运行一段时间,将属于阻塞状态
* 如果调用sleep方法之后,没有其他等待执行的线程,当前线程不会马上恢复执行
* 预备
* 3、2、1、Go!
*/
public class TestSleep1 {
public static void main(String[] args) {
System.out.println("预备...");
for (int i = 3; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);//毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Go!");
}
}
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 模拟时间打印
*/
public class TestSleep2 {
public static void main(String[] args) {
DateFormat df = new SimpleDateFormat("HH:mm:ss");
while (true) {
Date date = new Date();
String s = df.format(date);
System.out.println(s);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
放弃yield
/**
* yield
* 放弃
* 当前线程放弃时间片,回到就绪状态,竞争下一次时间片
* 如果调用yield方法之后,如果没有其他线程等待,当前线程会马上恢复运行
*/
public class TestYield {
public static void main(String[] args) {
FirstThread ft = new FirstThread();
SecondThread st = new SecondThread();
ft.start();
st.start();
}
}
class FirstThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("1Thread");
Thread.yield();
}
}
}
class SecondThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("2Thread");
Thread.yield();
}
}
}
加入join
/**
* join
* 加入
* 允许其他线程加入到当前线程中。当前线程会阻塞,直到加入线程执行完毕
*
* 【注】在start方法之后调用
*/
public class TestJoin extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName());
}
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
System.out.println("main...");
if (i == 5) {
//插入线程
TestJoin tJoin = new TestJoin();
tJoin.setName("程咬金线程出现了!");
tJoin.start();
try {
tJoin.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
优先级
/**
* 乌龟线程
*/
public class TortoiseThread extends Thread{
@Override
public void run() {
while (true) {
System.out.println("乌龟领先了!"+"线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
}
}
}
/**
* 1、如何查看线程优先级
* getPriority()
* 2、如何修改优先级
* setPriority()
* 【注】在start之前设置
* 3、线程优先级
* 最小优先级1
* 默认优先级5
* 最大优先级10
*/
public class TestThread {
public static void main(String[] args) {
//乌龟线程
TortoiseThread tortoiseThread = new TortoiseThread();
tortoiseThread.setName("王八线程");
tortoiseThread.setPriority(9);
tortoiseThread.start();
//兔子线程
Thread.currentThread().setName("兔子线程");
Thread.currentThread().setPriority(1);
while (true) {
System.out.println("兔子领先了!"+"线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
}
}
}
守护线程
/**
* 守护线程
* 线程中有两类:用户线程(前台线程)、守护线程(后台线程)
* 如果程序中所有前台线程都执行完毕了,后台线程会自动结束
* 例如:垃圾回收器 gc,就是一种守护线程
* setDaemon(true)设置为守护线程
*
*/
public class TestDaemon extends Thread{
@Override
public void run() {
while (true) {
System.out.println(this.getName());
}
}
public static void main(String[] args) {
//创建子线程
TestDaemon td = new TestDaemon();
//设置为守护线程 不设置守护线程会一直运行 设置后,主线程运行完,守护线程结束
td.setDaemon(true);
td.start();
for (int i = 0; i < 100; i++) {
System.out.println("main...");
}
}
}
优化昨日12306售票
/**
* 1、利用同步代码块完成线程同步操作
* synchronized (临界资源对象){
* //对临界资源对象加锁
* }
*/
public class TicketRunnable implements Runnable{
private int tickNum = 1000;
@Override
public void run() {
while (true) {
//参数,临界资源对象,引用数据类型
synchronized (this) {
if (tickNum <= 0) {
break;
}else {
//售票
System.out.println(Thread.currentThread().getName()+"窗口,"+"票:"+tickNum);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票数-1
tickNum--;
}
}
}
}
}
/**
* 模拟12306售票
* 线程同步优点:保障安全 缺点:牺牲性能
*/
public class TestTicket {
public static void main(String[] args) throws Exception {
//创建四个窗口
TicketRunnable tr = new TicketRunnable();
Thread t1 = new Thread(tr,"1号");
Thread t2 = new Thread(tr,"2号");
Thread t3 = new Thread(tr,"3号");
Thread t4 = new Thread(tr,"4号");
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
//join方法
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("程序结束");
}
}
/**
* 2、利用同步方法完成线程同步操作
* synchronized 返回值名称 方法名(形参列表){
*
* }
*/
public class TicketRunnable implements Runnable{
private int tickNum = 1000;
@Override
public void run() {
while (true) {
if (sellOne()) {
break;
}
}
}
public synchronized boolean sellOne() {
if (tickNum <= 0) {
return true;//票卖完了
}else {
//售票
System.out.println(Thread.currentThread().getName()+"窗口,"+"票:"+tickNum);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票数-1
tickNum--;
return false;
}
}
}
死锁
死锁产生的原因
同步嵌套,同步中还有同步,两个同步不是同一把锁
死锁出现的四个条件
互斥条件:资源不能被共享,只能被一个线程使用。
请求与保持条件:已经得到线程资源还可申请新的资源。
非剥夺条件:已经被分配资源的锁,不能在相应的线程强行剥夺。
循环等待:在系统中若干循环形成环路,导致环路中的每个线程都等待相邻线程释放资源。
生产者消费者(等待唤醒机制的经典案例)
/**
*如果是if判断
* 生成产品未消耗就又生成了
*分析原因:
* A.本方唤醒本方
* B.被唤醒的本方没有生成标识符继续生成
* 解决方案:
* 1.把if修改为while
* 出现死锁现象
* 死锁现象的原因:本方唤醒本方,被唤醒的本方继续判断标识符,继续等待,一直等待
* 解决方案:
* notify() 换成 notifyAll();//唤醒所有
*/
public class ProCusPractice {
public static void main(String[] args) {
//创建对象
Source source = new Source();
Producer2 producer2 = new Producer2(source);
Customer2 customer2 = new Customer2(source);
//创建线程
Thread t1 = new Thread(producer2);//生成者线程
Thread t2 = new Thread(customer2);//消费者线程
Thread t3=new Thread(producer2);//生成者线程
Thread t4=new Thread(producer2);//消费者线程
//启动
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/**
* 资源类
* 重点:生产完通知消费者消费
* 消费完通知生成者生产
*/
class Source{
//资源名
private String name;
//判断标志 标志位:代表有没有资源
private boolean flag;
public synchronized void set(String name) {
while(flag) {//true代表有资源,有资源就不生产 生产者进行等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有资源就生成
this.name = name;
//生成完资源改一下标志位,有商品了
flag = true;
System.out.println(Thread.currentThread().getName()+"生产了");
//生产完了唤醒消费者
notifyAll();
}
public synchronized void get() {
System.out.println("消费了:"+this.name);
while(!flag) {
//没有资源,消费者等待生产
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"消费了");
//改一下标志位 消费完没有资源了
flag = false;
//消费完后 唤醒生产者
notifyAll();
}
}
/**
* 生产者线程
* 生产!!!!
*/
class Producer2 implements Runnable{
//生产者生产
private Source source;
public Producer2(Source source) {
this.source = source;
}
@Override
public void run() {
while (true) {
//生产产品
source.set("面包");
}
}
}
/**
* 消费者线程
* 消费!!!!
*/
class Customer2 implements Runnable{
//消费者消费
private Source source;
public Customer2(Source source) {
this.source = source;
}
@Override
public void run() {
while (true) {
//消费产品
source.get();
}
}
}
高级多线程
/**
* 常用的线程池接口和类(所在包java.util.concurrent):
* Executor:线程池的顶级接口。
ExecutorService:线程池接口,可通过submit(Runnable task) 提交任务代码。
Executors工厂类:通过此类可以获得一个线程池。
通过 newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,无上限。
newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
newScheduledThreadPool(int corePoolSize) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
*/
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//1、创建一个线程 定长线程池newFixedThreadPool
ExecutorService es = Executors.newFixedThreadPool(4);
//1.创建一个线程 缓冲线程池 线程个数由任务决定 newCachedThreadPool
//ExecutorService es = Executors.newCachedThreadPool();
//1.创建一个线程 单线程 线程池 newSingleThreadExecutor
//ExecutorService es =Executors.newSingleThreadExecutor();
//1.创建一个线程 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。newScheduledThreadPool
//ExecutorService es = Executors.newScheduledThreadPool(4);
//2、任务
Runnable runnable = new Runnable() {
private int ticNum=100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticNum <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"窗口卖了"+ticNum+"张票。");
ticNum--;
}
}
}
};
//3、提交任务
for (int i = 0; i < 4; i++) {
es.submit(runnable);
}
//4、关闭线程池
es.shutdown();//等待 所有任务都执行完,然后关闭
// es.shutdownNow()//立即关闭 可能导致任务执行失败
// boolean isTerminated()如果关闭后所有任务已完成,则返回true
while (!es.isTerminated()) {
}
System.out.println("程序结束");
}
}

浙公网安备 33010602011771号