多线程
线程简介
-
程序:指令和数据的有序集合,只是个静态的概念。
-
进程是执行程序的一次执行过程,作为一个动态概念,是系统资源分配的单位。
-
一个进程可包含多个线程且最少有一个线程
-
真正的多线程是指有多个cpu即多核,如服务器,如果是模拟出来的多线程,本质上还是处于同一个cpu,同一时间单位只能执行一项代码任务,不能同时进行,只是切换的快产生的错觉。
-
线程是独立的执行路径。
-
在进程中开辟多线程,线程运行由调度器安排调度,不能人为干预。
-
资源抢夺问题,需加入并发控制。
继承Thread类
package Thread.lesson1;
//创建线程的方法一:继承Thread类,重写run方法,调用start开启线程
//注:线程开启不一定立即执行,由cpu调度
public class Dom1 extends Thread{
@Override
public void run(){
//run 方法线程体
for (int i = 0 ; i < 20 ; i ++) {
System.out.println("Threadstudy"+i);
}
}
public static void main(String[] args) {
//main 线程 主线程
//创建一个线程对象
Dom1 dom1 = new Dom1();
//调用run方法只会按照顺序执行,而start方法可以由cpu调度多线程同时交互进行
//dom1.run();
//调用start 方法开启线程,
dom1.start();
//写个测试的看效果
for (int i = 0 ; i < 1000 ; i ++){
System.out.println("Thread------" + i);
}
}
}
网图下载
import java.net.URL;
public class dom1 extends Thread {
private String url;
private String name;
//构造器
public dom1(String url,String name){
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public void run() {
webDown webdown = new webDown();
try {
webdown.down(url,name);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("下载了文件名为" + name);
}
public static void main(String[] args) {
//朕的头像
dom1 t1 = new dom1("http://i2.hdslb.com/bfs/face/536ff5e823cb3c354a71880edb3a15c826d5634d.jpg",
"536ff5e823cb3c354a71880edb3a15c826d5634d.jpg");
//狂神头像
dom1 t2 = new dom1("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg",
"83bb511365da513c55aa3d1958524f3b7db40684.jpg");
//der园长头像
dom1 t3 = new dom1("http://i1.hdslb.com/bfs/face/f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg",
"f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg");
//执行线程
t1.start();
t2.start();
t3.start();
}
}
//下载器
class webDown{
public void down(String url,String name) throws IOException {
//文件工具类:FileUtils
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,down方法有毛病");
}
}
}
执行下载结果并不是按照从上到下的顺序执行,原因同上。
实现Runnable接口
- 相比Thread类,Runnable更 推荐使用,都避免了单继承局限性,但Runnable跟灵活,能够使一个对象被多个线程使用。
使用Runnable修改上面两个例子
//创建线程方法2:实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class dom2 implements Runnable{
@Override
//run方法线程体
public void run() {
for (int i = 0; i < 20 ; i++){
System.out.println("看代码" +i);
}
}
public static void main(String[] args) {
//创建Runnable接口的实现对象
dom2 dom2 = new dom2();
//创建线程对象,通过线程对象开始线程——代理
//Thread thread = new Thread();
//thread.start();
// 等同于以上两句代码,简写为:
new Thread(dom2).start();
for (int i = 0; i < 1000 ; i++){
System.out.println("学习" +i);
}
}
}
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class dom1 implements Runnable {
private String url;
private String name;
//构造器
public dom1(String url,String name){
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public void run() {
webDown webdown = new webDown();
try {
webdown.down(url,name);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("下载了文件名为" + name);
}
public static void main(String[] args) {
//朕的头像
dom1 t1 = new dom1("http://i2.hdslb.com/bfs/face/536ff5e823cb3c354a71880edb3a15c826d5634d.jpg",
"536ff5e823cb3c354a71880edb3a15c826d5634d.jpg");
//狂神头像
dom1 t2 = new dom1("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg",
"83bb511365da513c55aa3d1958524f3b7db40684.jpg");
//der园长头像
dom1 t3 = new dom1("http://i1.hdslb.com/bfs/face/f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg",
"f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg");
//执行线程
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
//下载器
class webDown{
public void down(String url,String name) throws IOException {
//文件工具类:FileUtils
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,down方法有毛病");
}
}
}
初识并发问题
//多个线程同时操作一个对象
//买火车票的例子
//执行完毕发现问题,多个线程执行同一个资源时,线程不安全,数据紊乱。即并发问题
public class dom3 implements Runnable{
@Override
public void run() {
while (true){
if(Nums <= 0){
break;
}
try {
//模拟延时,延时0.2s
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Thread.currentThread().getName() : 当前线程名
System.out.println(Thread.currentThread().getName()+"拿到了第" +Nums--+"票");
}
}
//票数
private int Nums = 10;
public static void main(String[] args) {
dom3 dom3 = new dom3();
//可以给每个线程起名
new Thread(dom3,"二狗").start();
new Thread(dom3,"老刘").start();
new Thread(dom3,"黄牛").start();
}
}
龟兔赛跑
//模拟龟兔赛跑(经典笔试题)
//定义一个赛道距离,离终点越来越近
//判断比赛是否结束
//打印出胜利者
//比赛开始
//兔子得睡会
//王八赢了
public class dom4 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//让兔子睡个觉,每十步睡一会,睡0.01s就够,不能一开始就睡
if(Thread.currentThread().getName().equals("兔子")&& i%10==0 &&i!=0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//比赛结束
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+ i + "步");
}
}
//胜利者-静态
private static String Winner;
//判断是否完成比赛
private boolean gameOver(int stops) {
if (Winner != null) {
//已经存在Winner
return true;
}
{
if (stops >= 100) {
Winner = Thread.currentThread().getName();
System.out.println("winner is" + Winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
dom4 dom4 = new dom4();
new Thread(dom4,"兔子").start();
new Thread(dom4,"乌龟").start();
}
}
实现Callable接口 *
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建方式三:Callable接口
//可以定义返回值,抛出异常
public class dom5 implements Callable<Boolean> {
private String url;
private String name;
//构造器
public dom5(String url,String name){
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public Boolean call() {
webDown webdown = new webDown();
try {
webdown.down(url,name);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("下载了文件名为" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//朕的头像
dom5 t1 = new dom5("http://i2.hdslb.com/bfs/face/536ff5e823cb3c354a71880edb3a15c826d5634d.jpg",
"536ff5e823cb3c354a71880edb3a15c826d5634d.jpg");
//狂神头像
dom5 t2 = new dom5("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg",
"83bb511365da513c55aa3d1958524f3b7db40684.jpg");
//der园长头像
dom5 t3 = new dom5("http://i1.hdslb.com/bfs/face/f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg",
"f85fbedb0a6bfebedba167791a816e1b36f757d9.jpg");
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdown();
}
}
//下载器
class webDown{
public void down(String url,String name) throws IOException {
//文件工具类:FileUtils
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,down方法有毛病");
}
}
}
静态代理
//代理结婚
//代理模式:真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色,代理对象可以做代理对象做不了的事,真实对象专注做自己的事
//线程的底部实现思想
public class dom6 {
public static void main(String[] args) {
//WeddingCompany weddingCompany = new WeddingCompany(new You());
//weddingCompany.HappyMarry();
new WeddingCompany(new You()).HappyMarry();//精简版
}
}
//interface : 接口
interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("俺结婚啦");
}
}
//代理角色
class WeddingCompany implements Marry{
//代理-》真实目标角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry() {
after();
this.target.HappyMarry();
before();
}
private void after(){
System.out.println("布置会场");
}
private void before(){
System.out.println("收尾款");
}
}
Lamda表达式
简写代码
/*
推导lamda表达式
*/
public class dom7 {
//静态内部类
static class lamda2 implements Lamda{
@Override
public void lamDa() {
System.out.println("i like lamda2");
}
}
public static void main(String[] args) {
Lamda la = new Lam();
la.lamDa();
la = new lamda2();
la.lamDa();
//局部内部类
class lamda3 implements Lamda{
@Override
public void lamDa() {
System.out.println("i like lamda3");
}
}
la = new lamda3();
la.lamDa();
//匿名内部类,没有类的名称,必须借助接口或父类
la = new Lamda() {
@Override
public void lamDa() {
System.out.println("i like lamda4");
}
};
la.lamDa();
//用Lamda简化
//lamda一步步简化,简化参数类型,括号,花括号
//lamda表达式只能有一行时才能简化为一行,多行需要{}包裹
//必须是函数式接口(接口只有一个方法)
//多个参数也能去掉参数类型,要去都去掉;
la = () ->{
System.out.println("i like lamda5");
};
la.lamDa();
la = () ->System.out.println("i like lamda6");
la.lamDa();
}
}
//定义一个函数式接口
interface Lamda{
void lamDa();
}
//实现类
class Lam implements Lamda{
@Override
public void lamDa() {
System.out.println("i like lamda");
}
}
线程停止
- 线程五大状态:
-
- 创建状态:new 即新生状态
- 就绪状态:调用start()方法后进入
-
- 阻塞状态:运行中调用睡眠或锁定状态,状态结束重新进入就绪状态
- 运行状态:真正执行线程体代码块
- 死亡状态:线程中断或结束
//建议线程自己停止
//不建议死循环或stop,destroy即jdk不建议的党费强制停止
public class dom8 implements Runnable{
//设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run---Thread"+i);
i++;
}
}
//设置一个公开的方法停止线程,换标志位
public void stop(){
this.flag= false;
}
public static void main(String[] args) {
dom8 dom8 = new dom8();
new Thread(dom8).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i == 900){
//调用stop方法切换标致,停止线程
dom8.stop();
System.out.println("run线程该停止了");
}
}
}
}
线程休眠-sleep
- sleep:单位-毫秒(1秒=1000毫秒);
- 需要抛出异常
- 每个对象都有一把锁,sleep不会释放锁 *
//模拟网络延时,放大问题的发生性,回到上面买票问题
public class dom9 implements Runnable{
@Override
public void run() {
while (true){
if(Nums <= 0){
break;
}
try {
//模拟延时,延时0.2s
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Thread.currentThread().getName() : 当前线程名
System.out.println(Thread.currentThread().getName()+"拿到了第" +Nums--+"票");
}
}
//票数
private int Nums = 10;
public static void main(String[] args) {
dom3 dom3 = new dom3();
//可以给每个线程起名
new Thread(dom3,"二狗").start();
new Thread(dom3,"老刘").start();
new Thread(dom3,"黄牛").start();
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class dom9{
public static void main(String[] args) {
//倒计时
// try {
// TimeDown();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//打印当前时间
Date date = new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis());//更新时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void TimeDown() throws InterruptedException {
int time = 10;
while (true) {
Thread.sleep(100);
System.out.println(time--);
if (time<=0){
break;
}
}
}
}
线程礼让-yield
//将进程从运行状态转为就绪状态,但不阻塞
//礼让不一定成功,看cpu心情
public class dom10 {
public static void main(String[] args) {
myYield myYield = new myYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class myYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程强制执行-join
//join方法,可理解为强行插队,阻塞其它进程,霸道!
public class dom11 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("vip来了"+i);
}
}
public static void main(String[] args) {
//启动线程
dom11 dom11 = new dom11();
Thread thread = new Thread(dom11);
thread.start();
//主线程
for (int i = 0; i < 500; i++) {
if (i == 200){
try {
thread.join();//插队,让当前线程先靠边等等,给其他线程先走
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main"+i);
}
}
}
观测线程状态-state
//观察测试线程的状态
public class dom12 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
//线程启动五秒
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("`````");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state); //NEW
//观察启动后
thread.start();
state = thread.getState();
System.out.println(state); //RUNNABLE
while (state!=Thread.State.TERMINATED){//线程不终止,一直处于输出状态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();//更新线程状态
System.out.println(state);//输出状态
}
}
}
线程的优先级-Priority
//测试线程优先级
//优先级范围1-10;超出范围会报错。优先级越高,cpu给到资源越多,不是绝对的!
public class dom13 {
public static void main(String[] args) {
//主线程默认优先级-5
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
Thread t7 = new Thread(myPriority);
Thread t8 = new Thread(myPriority);
//先设置优先级,再启动!
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(6);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
t5.setPriority(Thread.NORM_PRIORITY);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"____"+Thread.currentThread().getPriority());
}
}
守护线程-daemon
//测试:一生守护你
//人生不过36500天
//程序不会等待守护线程执行完毕,程序必须确保用户线程执行完毕,即用户线程执行完守护线程仍在持续保护到进程结束。
public class dom14 {
public static void main(String[] args) {
dom dom = new dom();
You2 you2 = new You2();
Thread thread = new Thread(dom);
thread.setDaemon(true);//默认是flase,正常的都是用户线程
thread.start();//守护线程启动
new Thread(you2).start();
}
}
class dom implements Runnable{
@Override
public void run() {
while (true){
System.out.println("守护着你");
}
}
}
class You2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都要开开心心的生活");
}
System.out.println("---goodBye! word---");
}
}
线程同步机制
-
队列和锁
-
安全==性能降低(鱼与熊掌不可兼得)
三大不安全案例
//不安全的买票
//线程不安全,有负数--每个线程都有自己的独立工作区,各自内存内互不影响。
public class dom15{
public static void main(String[] args) {
BuyTicket buyTicket1 = new BuyTicket();
new Thread(buyTicket1,"赵").start();
new Thread(buyTicket1,"钱").start();
new Thread(buyTicket1,"孙").start();
new Thread(buyTicket1,"李").start();
}
}
//买票方法
class BuyTicket implements Runnable{
private int Nums = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
private void buy(){
if(Nums<=0){
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了"+Nums--);
}
}
//不安全取钱
//两人共同取钱
public class dom16 {
public static void main(String[] args) {
Account account = new Account(100,"基金");
back you = new back(account,50,"你");
back her = new back(account,100,"她");
you.start();
her.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money,String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class back extends Thread{
//账户
Account account ;
//现在有多少钱
int nowMoney;
//取多少钱
int DrawingMoney;
public back(Account account,int DrawingMoney,String name) {
super(name);
this.account = account;
this.DrawingMoney = DrawingMoney;
}
public void run(){
//判断钱够不够
if(account.money-DrawingMoney < 0){
System.out.println(Thread.currentThread().getName()+"钱不够");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - DrawingMoney ;
nowMoney = nowMoney + DrawingMoney;
System.out.println(this.getName()+"--现在有--"+ nowMoney);
System.out.println(account.name+"余额为"+account.money);
}
}
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合,难以达到1000条线程全部创建出来的目的
//注意系统能创建的线程数有限
public class dom17 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>() ;
for(int i = 0;i< 1000;i++) {
new Thread(()->list.add(Thread.currentThread().getName())).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
同步方法及同步块-synchronized
对以上三个不安全案例进行优化修改-加锁
//不安全的买票
//线程不安全,有负数
public class dom15{
public static void main(String[] args) {
BuyTicket buyTicket1 = new BuyTicket();
new Thread(buyTicket1,"赵").start();
new Thread(buyTicket1,"钱").start();
new Thread(buyTicket1,"孙").start();
new Thread(buyTicket1,"李").start();
}
}
//买票方法
class BuyTicket implements Runnable{
private int Nums = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
//synchronized 同步方法,锁的是this,保证线程安全
private synchronized void buy(){
if(Nums<=0){
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了"+Nums--);
}
}
//不安全取钱
//两人共同取钱
public class dom16 {
public static void main(String[] args) {
Account account = new Account(100,"基金");
back you = new back(account,50,"你");
back her = new back(account,100,"她");
you.start();
her.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money,String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class back extends Thread{
//账户
Account account ;
//现在有多少钱
int nowMoney;
//取多少钱
int DrawingMoney;
public back(Account account,int DrawingMoney,String name) {
super(name);
this.account = account;
this.DrawingMoney = DrawingMoney;
}
public void run(){
//同步块
//关键在于锁的对象,锁的对象一定是变化的量,如不能锁银行,要锁银行卡
synchronized (account){
//判断钱够不够
if(account.money-DrawingMoney < 0){
System.out.println(Thread.currentThread().getName()+"钱不够");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - DrawingMoney ;
nowMoney = nowMoney + DrawingMoney;
System.out.println(this.getName()+"--现在有--"+ nowMoney);
System.out.println(account.name+"余额为"+account.money);
}
}
}
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合,难以达到1000条线程全部创建出来的目的
//注意系统能创建的线程数有限
public class dom17 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>() ;
for(int i = 0;i< 1000;i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
CopyOnWriteArrayList
- 安全类型的集合
import java.util.concurrent.CopyOnWriteArrayList;
//测试juc安全类型的集合
public class dom18 {
public static void main(String[] args) {
//并发包
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
- 对个线程都在等待对方释放资源,互相僵持
- 某个同步块有两个对象以上的锁时,易发生死锁问题
//死锁问题
public class dom19 {
public static void main(String[] args) {
new Thread(new MakeUp(1,"翠花")).start();
new Thread(new MakeUp(0,"麻花")).start();
}
}
//口红
class Lipstick{}
//镜子
class Mirror{}
class MakeUp extends Thread{
//资源只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
//选择器
int choise;
//角色
String name;
MakeUp(int choise,String name){
this.choise = choise;
this.name = name;
}
@Override
public void run() {
makeup();
}
//化妆,互相持有对方的锁
private void makeup(){
if(choise == 0){
//锁住当前资源
synchronized (lipstick){
System.out.println(this.name + "拿到口红");
try {
//一秒后拿另一个资源
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror){
System.out.println(this.name+"拿到镜子");
}
}
}else {
synchronized (mirror){
System.out.println(this.name + "拿到镜子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick){
System.out.println(this.name+"拿到口红");
}
}
}
}
}
- 上面例子两个对象互相占有对方所需资源且都在等待释放资源,出现死锁问题,将“锁中锁”拿出来即可解决问题。
//化妆,互相持有对方的锁
private void makeup(){
if(choise == 0){
//锁住当前资源
synchronized (lipstick){
System.out.println(this.name + "拿到口红");
try {
//一秒后拿另一个资源
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (mirror){
System.out.println(this.name+"拿到镜子");
}
}else {
synchronized (mirror){
System.out.println(this.name + "拿到镜子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lipstick){
System.out.println(this.name+"拿到口红");
}
}
}
Lock锁
-
lock:显式锁,需要手动开关
synchronized:隐式锁,出了作用域自动释放
-
lock:只有代码块锁,没有方法锁
-
lock锁性能更好,有更好的拓展性
import java.util.concurrent.locks.ReentrantLock;
//lock锁
public class dom20 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"老张").start();
new Thread(ticket,"二狗").start();
new Thread(ticket,"三炮").start();
}
}
class Ticket implements Runnable{
int Num = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if(Num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--拿到了第"+ Num-- +"张票");
}else {
break;
}
}finally {
lock.unlock();//释放锁
}
}
}
}
生产者消费者问题 *
管程法
//生产者消费者模型-》利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class dom21 {
public static void main(String[] args) {
SyContainer syContainer = new SyContainer();
new Productor(syContainer).start();
new Consumer(syContainer).start();
}
}
//生产者
class Productor extends Thread{
SyContainer container;
public Productor(SyContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了" + i +"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SyContainer container;
public Consumer(SyContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + container.pop().id + "只鸡");
}
}
}
//产品
class Chicken {
//产品编号
int id;
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SyContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
//容量计数器
int str = 0;
//生产者丢入产品
public synchronized void push(Chicken chicken){
//满了,生产stop
if(str == chickens.length){
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没满,生产ing
chickens[str] = chicken;
str++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断是否可以消费
if(str==0){
//wait
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
str--;
Chicken chicken = chickens[str];
//消费后通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法
//信号灯法-》通过标志位解决-flag
public class dom22 {
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;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
this.tv.play("猫和老鼠");
}else {
this.tv.play("抖音记录美好生活");
}
}
}
}
//消费者
class watcher extends Thread{
Tv tv;
public watcher(Tv tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品-节目
class Tv{
//演员表演,观众等待;观众观看,演员等待
String voice;
boolean flag = true;
//表演
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了:" +voice);
this.notifyAll();//通知,唤醒
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了"+ voice);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
- 参考callable接口
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//侧式线程池
public class dom23 {
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{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}