Java多线程(全)
Posted v587h
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程(全)相关的知识,希望对你有一定的参考价值。
## 多线程
### 1.程序、进程、线程
- 在操作系统中运行的程序就是进程;
- 一个进程可以有多个线程;
- 程序是指令和数据的有序集合,是一个静态的概念;
- 而进程则是执行程序的一次执行过程,是一个动态的概念;
- 进程是系统资源分配的单位
- 线程是CPU调度和执行的单位
- 真正的多线程是指多个cpu,即多核
理解:进程相当于一个类,类中有多个方法(即多个线程),main()方法即(主线程),gc线程。
- main()方法即(主线程)为系统的入口,用于执行整个程序;
- 线程的运行是由调度器安排调度,不能人为干预;
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu的调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
### 2.线程的创建
1. **三种创建方式**
- Thread class
- 继承Thread类
- Runnable接口
- 实现Runnable接口
- Callable接口
- 实现Callable接口
### **3.Thread**
- 自定义类继承Thread
- 重写run()方法;
- 创建对象,调用start()开启线程
```java
import javax.xml.transform.Source;
public class TestThread01 extends Thread{
//继承Thread类
//总结:线程开启不一定立即执行,有CPU调度执行
@Override
public void run() {
//run()方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("**********-4564646465");
}
}
//main线程,即主线程
public static void main(String[] args) {
TestThread01 testThread01 = new TestThread01();
//testThread01.start();//线程开启,交替(类似同时)执行,电脑单核只有一个cpu
testThread01.run();//这个在前,先执行run();
for (int i = 0; i < 20; i++) {
System.out.println("7554574545*********");
}
}
}
```
多线程实现多张图片同时下载
```java
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class TestThread02 extends Thread{
private String url;
private String name;
public TestThread02(String url,String name){
this.url=url;
this.name=name;
}
public TestThread02(){
}
@Override
public void run() {
//下载线程
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url,name);
System.out.println(name);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestThread02 testThread02 = new TestThread02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603037822768&di=5f0cc79b7c104781de8cea48c7d8b1ca&imgtype=0&src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2Fmonth_1012%2F10120514509c7244b23f4a2fa5.jpg","a.jpg");
TestThread02 testThread03 = new TestThread02("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00720-1746.jpg","a1.jpg");
TestThread02 testThread04 = new TestThread02("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2441956749,4275399915&fm=26&gp=0.jpg","a2.jpg");
testThread02.start();
testThread03.start();
testThread04.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name) throws IOException {
FileUtils.copyURLToFile(new URL(url),new File(name));
}
}
```
### 4.Runnable
推荐使用Runnable对象,因为Java单继承的局限性
1. 定义MyRunnable类实现Runnable接口
2. 实现run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程
```java
public class TestRunnable01 implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("*******************");
}
}
public static void main(String[] args) {
TestRunnable01 testRunnable01 = new TestRunnable01();
new Thread(testRunnable01).start();
}
}
```
### 5.小结
- 继承Thread
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
- 实现Runnable接口
- 实现接口Runnable具备多线程能力
- 启动线程:传入目标对象+Thread对象。start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
![](https://img-blog.csdnimg.cn/img_convert/355be8f423ab9860fea3b33ebbdceed2.png)
**多个线程操作同一个资源问题**
```java
//多个线程操作同一个资源
//模拟买票
//发现问题:多个线程操作同一个人资源的情况下,线程不安全,数据紊乱。
public class TestRunnable02 implements Runnable{
private int ticket=10;
public void run() {
while (true){
if (ticket<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticket--+"票");
}
}
public static void main(String[] args) {
TestRunnable02 runnable02=new TestRunnable02();
new Thread(runnable02,"小鹏").start();
new Thread(runnable02,"小强").start();
new Thread(runnable02,"小慧").start();
}
}
```
### **6.实例:龟兔赛跑**
```java
import org.omg.Messaging.SyncScopeHelper;
public class Race implements Runnable{
private static String winner;
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")&&i%50==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断100步
if (win(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
//判断胜利者
public boolean win(int step){
if (winner!=null){
return true;
}
if (step>=100){
winner=Thread.currentThread().getName();
System.out.println("胜利者是"+winner);
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
```
### 7.Callable
![](https://img-blog.csdnimg.cn/img_convert/9f4f7d5163fc8eea54b038ebb7339043.png)
### 8.静态代理模式
```java
//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实对象
//好处:
//代理对象可以做很多真实对象做不了的事情
//真实对象专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
WeCompany weCompany=new WeCompany(new You());
weCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{
public void HappyMarry() {
System.out.println("结婚");
}
}
class WeCompany implements Marry{
private Marry marry;
public WeCompany(Marry marry) {
this.marry = marry;
}
public void HappyMarry() {
before();
this.marry.HappyMarry();
after();
}
public void before(){
System.out.println("结婚之前,布置现场");
}
public void after(){
System.out.println("结婚之后,收尾款");
}
}
```
### 9.Lamda表达式
1. 函数式接口:只有一个抽象方法的接口
2. 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
3. lambda推导过程代码
```java
import java.awt.peer.LabelPeer;
/*
推到lambda表达式
*/
public class TestLambda {
//3.静态内部类
static class Like2 implements ILikeLamb{
public void lambda() {
System.out.println("i like lambda2***");
}
}
public static void main(String[] args) {
//方式1使用外部类
ILikeLamb like = new Like1();
like.lambda();
//方式2使用静态内部类
like=new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILikeLamb{
public void lambda() {
System.out.println("i like lambda3!!!");
}
}
//方式3使用局部内部类
like=new Like3();
like.lambda();
//5.匿名内部类
like=new ILikeLamb() {
public void lambda() {
System.out.println("i like lambda4~~~~~~");
}
};
//方式4使用匿名内部类
like.lambda();
//6.lambda表达式
like=() -> {
System.out.println("i like lambda5~***~~");
};
//方式5使用lambda表达式
like.lambda();
}
}
//1.定义一个函数式接口
interface ILikeLamb{
void lambda();
}
//2.实现类
class Like1 implements ILikeLamb{
public void lambda() {
System.out.println("i like lambda");
}
}
```
**lambda简化**
```java
class TestLambda2 {
public static void main(String[] args) {
//局部内部类
class Love implements ILove{
@Override
public void live(int a) {
System.out.println("i love number is"+a);
}
}
ILove love=new Love();
love.live(666);
//匿名内部类
love=new ILove() {
@Override
public void live(int a) {
System.out.println("i love number is"+a);
}
};
love.live(888);
//lambda表达式
love=(int a)-> {
System.out.println("i love number is"+a);
};
love.live(999);
//简化1,去参数类型
love=(a)-> {
System.out.println("i love number is"+a);
};
//简化2.去括号
love=a-> {
System.out.println("i love number is"+a);
};
//简化3.去花括号
love = a -> System.out.println("i love number is"+a);
//总结:
//方法中只有一行代码才可以简化花括号,多行得用{};
//前提接口必须是函数式接口;
//多个参数得同时都去掉类型,却得用()括起来(a,b,c);
}
}
interface ILove{
public void live(int a);
}
```
### 10.线程状态
**五种状态**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021135526337.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
两张图对比**理解**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021135843429.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
### **11.线程方法**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021135905969.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
### 12.线程停止
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021135939891.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
自定义停止代码
```java
public class ThreadStop implements Runnable{
private boolean flg=true;
@Override
public void run() {
int i=0;
while (flg){
System.out.println("running**********"+i++);
}
}
public static void main(String[] args) {
ThreadStop threadStop=new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i < 500; i++) {
System.out.println("main线程"+i);
if (i==444){
threadStop.stop();
System.out.println("该线程停止了***************");
}
}
}
public void stop(){
this.flg=false;
}
}
```
### 13.线程休眠
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021140010844.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
**模拟倒计时**
```java
//模拟倒计时
public class ThreadSleep2 implements Runnable{
private int ticket=10;
@Override
public void run() {
while (true){
if (ticket <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(1000);
System.out.println(ticket--);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadSleep2 threadSleep2=new ThreadSleep2();
new Thread(threadSleep2).start();
}
}
```
**模拟时钟**
```java
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟时钟
import static java.lang.System.currentTimeMillis;
public class ThreadSleep3 {
public static void main(String[] args) {
//获取当前系统时间
Date time= new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
time= new Date(System.currentTimeMillis());//更新时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
```
### 14.线程礼让
- 礼让线程,让当前正在执行的线程暂停,但步阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!看CPU心情
```java
//礼让不一定成功
public class ThreadYield {
public static void main(String[] args) {
MyYield myYield=new MyYield();
new Thread(myYield,"aaa").start();
new Thread(myYield,"bbb").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"停止执行");
}
}
```
### 15.Join
- Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想象成插队
```java
//测试join//类似于插队
public class ThreadJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"来了"+i);
}
}
public static void main(String[] args) {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread=new Thread(threadJoin,"vip");
thread.start();
for (int i = 0; i < 100; i++) {
if (i==40){
try {
thread.join();//强势插入,主线程阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main来了"+i);
}
}
}
```
### 16.线程状态观测
- ```
Thread.State
```
- 线程可以处于以下状态之一:
- [`NEW`](../../java/lang/Thread.State.html#NEW)
尚未启动的线程处于此状态。
- [`RUNNABLE`](../../java/lang/Thread.State.html#RUNNABLE)
在Java虚拟机中执行的线程处于此状态。
- [`BLOCKED`](../../java/lang/Thread.State.html#BLOCKED)
被阻塞等待监视器锁定的线程处于此状态。
- [`WAITING`](../../java/lang/Thread.State.html#WAITING)
正在等待另一个线程执行特定动作的线程处于此状态。
- [`TIMED_WAITING`](../../java/lang/Thread.State.html#TIMED_WAITING)
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
- [`TERMINATED`](../../java/lang/Thread.State.html#TERMINATED)
已退出的线程处于此状态。
### 17.线程的优先级
set和get--priority()
低优先级只意味着调度概率低,并不是优先级低就不会被先调用。
测试代码
```java
public class ThreadPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);
Thread thread3=new Thread(myThread);
Thread thread4=new Thread(myThread);
Thread thread5=new Thread(myThread);
//设置优先级
thread1.setPriority(6);
thread1.start();
thread2.setPriority(2);
thread2.start();
thread3.setPriority(8);
thread3.start();
thread4.setPriority(4);
thread4.start();
thread5.setPriority(10);
thread5.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
}
```
### 18.守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等
```java
public class ThreadDaemon {
public static void main(String[] args) {
Gad gad=new Gad();
Our our=new Our();
Thread t=new Thread(gad);
Thread t1=new Thread(our);
t.setDaemon(true);//用户线程默认false
t.start();
t1.start();
}
}
class Gad implements Runnable{
@Override
public void run() {
while (true){
System.out.println("Gad NB alone living");
}
}
}
class Our implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("living*********living"+i);
}
System.out.println("death---------death=-");
}
}
```
### 19.线程同步
1. 多个线程操作同一个资源
2. 并发:同一个对象被多个线程同时操作
3. 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程在再使用
4. 队列+锁:锁机制(synchronized)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJT2Wokt-1603259602918)(C:UsersWv587-hAppDataRoamingTypora ypora-user-imagesimage-20201020163000351.png)]
**性能倒置**
### 20.三大线程不安全列子
1. 购票;
```java
package safe;
public class UnSafeTicket {
public static void main(String[] args) {
BuyTicket buy=new BuyTicket();
new Thread(buy,"小张").start();
new Thread(buy,"小五").start();
new Thread(buy,"小六").start();
new Thread(buy,"小巴").start();
}
}
class BuyTicket implements Runnable{
private int tickets=100;
private boolean flag=true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (tickets<=0){
flag=false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到第"+tickets--+"票");
}
}
```
2. 取款;
```java
package safe;
import com.sun.org.apache.bcel.internal.generic.NEW;
import javax.naming.Name;
public class UnSafeBank {
public static void main(String[] args) {
Account account= new Account("联邦",9999999);
Bank bank=new Bank(account,5111111);
Bank bank2=new Bank(account,5111111);
Thread t=new Thread(bank,"多多");
t.start();
Thread t2=new Thread(bank2,"零零");
t2.start();
}
}
class Account{
private String account;
private int money;
public Account(String account, int money) {
this.account = account;
this.money = money;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
class Bank implements Runnable{
Account account;
int drawMoney;
int nowMoney;
public Bank(Account account, int drawMoney) {
this.account = account;
this.drawMoney = drawMoney;
}
@Override
public void run() {
if (account.getMoney()-drawMoney<0){
System.out.println("余额不足!!!");
return;
}
try {
Thread.sleep(1000);//等待让两个线程都进来
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.getMoney()-drawMoney);
nowMoney=nowMoney+drawMoney;
System.out.println(Thread.currentThread().getName()+"取走了"+nowMoney);
System.out.println(account.getAccount()+"卡上余额为:"+account.getMoney());
}
}
```
3. 集合list;
```java
package safe;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list=new ArrayList<String>();
for (int i = 0; i < 10010; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
```
### 21.synchronized
- 同步方法
```java
package safe;
public class SafeTicket {
public static void main(String[] args) {
BuyTicket1 buy=new BuyTicket1();
new Thread(buy,"小张").start();
new Thread(buy,"小五").start();
new Thread(buy,"小六").start();
new Thread(buy,"小巴").start();
}
}
class BuyTicket1 implements Runnable{
private int tickets=100;
private boolean flag=true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//在方法加synchronized
private synchronized void buy() throws InterruptedException {
if (tickets<=0){
flag=false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到第"+tickets--+"票");
}
}
```
- 同步块
```java
package safe;
public class SafeBank {
public static void main(String[] args) {
Account1 account= new Account1("联邦",9999999);
Bank1 bank=new Bank1(account,5111111);
Bank1 bank2=new Bank1(account,5111111);
Thread t=new Thread(bank,"多多");
t.start();
Thread t2=new Thread(bank2,"零零");
t2.start();
}
}
class Account1{
private String account;
private int money;
public Account1(String account, int money) {
this.account = account;
this.money = money;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
class Bank1 implements Runnable{
Account1 account;
int drawMoney;
int nowMoney;
public Bank1(Account1 account, int drawMoney) {
this.account = account;
this.drawMoney = drawMoney;
}
@Override
public void run() {
//同步块,锁 变化的对象
synchronized(account){ //锁的对象就是变化的量,需要增删改
if (account.getMoney()-drawMoney<0){
System.out.println("余额不足!!!");
return;
}
try {
Thread.sleep(1000);//等待让两个线程都进来
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.getMoney()-drawMoney);
nowMoney=nowMoney+drawMoney;
System.out.println(Thread.currentThread().getName()+"取走了"+nowMoney);
System.out.println(account.getAccount()+"卡上余额为:"+account.getMoney());
}
}
}
```
- 对比JUC
```java
package safe;
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> co=new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
co.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(2000);
System.out.println(co.size());
}
}
```
### 22.死锁
- 多个线程各自占有一些资源,并且互相等待其他线程占有的资源才能运行,而导致两个过着多个线程都在等待对方释放资源,都停止执行的情况
```java
package thread;
//多个资源互相抱着对方的资源,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup makeup=new Makeup(0,"小红");
Makeup makeup1=new Makeup(1,"大蓝");
makeup.start();
makeup1.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
//static确保资源唯一
static private Lipstick lipstick=new Lipstick();
static private Mirror mirror=new Mirror();
int choose;
String name;
public Makeup(int choose, String name) {
this.choose = choose;
this.name = name;
}
public void run(){
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choose==0){
synchronized (lipstick){
System.out.println(this.name+"拿到口红!!");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.name+"拿到镜子!!!");
}
}
/*synchronized (mirror){
System.out.println(this.name+"拿到镜子!!!");
}*/
}else {
synchronized (mirror){
System.out.println(this.name+"拿到镜子!!");
Thread.sleep(2000);
synchronized (lipstick){
System.out.println(this.name+"拿到口红!!!");
}
}
/* synchronized (lipstick){
System.out.println(this.name+"拿到口红!!!");
}*/
}
}
}
```
- 产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞是,对已获得的资源保持不放。
- 不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
> 上面列出的四个条件,只要想办法破其中的任意一个或多个就可以避免死锁发生
### 23.Lock
1. 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
2. java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
3. 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
4. ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
> **Lock的使用**
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102114010564.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
> 测试代码
```java
package safe;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TicketLock ti=new TicketLock();
new Thread(ti).start();
new Thread(ti).start();
new Thread(ti).start();
}
}
class TicketLock implements Runnable{
int ticket=10;
private final ReentrantLock lock=new ReentrantLock();//创建锁对象
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticket--);
} else {
return;
}
} finally {
lock.unlock();//解锁
}
}
}
}
```
> **synchronized与Lock的对比**
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序:
- Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
### 24.生产者消费者
> 代码
```java
package safe;
import java.util.LinkedList;
public class TestPC {
//最大容量
public static final int MAX_SIZE = 10;
//存储媒介
public static LinkedList<Integer> list = new LinkedList<>();
class Producer implements Runnable {
@Override
public void run() {
synchronized (list) {
//仓库容量已经达到最大值
while (list.size() == MAX_SIZE) {
System.out.println("仓库已满,生产者" + Thread.currentThread().getName() + "不可生产.");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(1);
System.out.println("生产者" + Thread.currentThread().getName() + "生产, 仓库容量为" + list.size());
list.notify();
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
synchronized (list) {
while (list.size() == 0) {
System.out.println("仓库为空,消费者" + Thread.currentThread().getName() + "不可消费.");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.removeFirst();
System.out.println("消费者" + Thread.currentThread().getName() + "消费,仓库容量为" + list.size());
list.notify();
}
}
}
public static void main(String[] args) {
TestPC pc = new TestPC();
Producer producer = pc.new Producer();
Consumer consumer = pc.new Consumer();
for (int i = 0; i < 10; i++) {
Thread pro = new Thread(producer,"P");
pro.start();
Thread con = new Thread(consumer,"C");
con.start();
}
}
}
```
### 25.线程池
> 使用线程池
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021140136559.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdodWkxaGFv,size_16,color_FFFFFF,t_70#pic_center)
> 代码
```java
package thread;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
//创建服务,创建线程池,newFixedThreadPool,参数:个数
ExecutorService service= Executors.newFixedThreadPool(5);
service.execute(new MyThread1());
service.execute(new MyThread1());
service.execute(new MyThread1());
service.execute(new MyThread1());
service.execute(new MyThread1());
//关闭服务
service.shutdown();
}
}
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
```
### 26.总结
> 三种方法
```JAVA
package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ThreadNew {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1
Thread1 t1=new Thread1();
t1.start();
//2.
Thread2 t2=new Thread2();
new Thread(t2).start();
FutureTask<Integer> task=new FutureTask<Integer>(new Thread3());
new Thread(task).start();
Integer integer=task.get();
System.out.println(integer);
}
}
//1.继承Thread
class Thread1 extends Thread{
public void run(){
System.out.println("Thread111");
}
}
//2.实现Runnable
class Thread2 implements Runnable{
@Override
public void run() {
System.out.println("thread222");
}
}
//3.实现Callable
class Thread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("thread3333");
return 100;
}
}
```
以上是关于Java多线程(全)的主要内容,如果未能解决你的问题,请参考以下文章
面试腾讯,字节跳动首先要掌握的Java多线程,一次帮你全掌握!