码迷,mamicode.com
首页 > 编程语言 > 详细

Java多线程(全)

时间:2020-10-21 21:08:15      阅读:23      评论:0      收藏:0      [点我收藏+]

标签:sim   表达式   row   静态代理   star   fileutils   ESS   小强   避免   

## 多线程

### 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:\Users\Wv587-h\AppData\Roaming\Typora\typora-user-images\image-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多线程(全)

标签:sim   表达式   row   静态代理   star   fileutils   ESS   小强   避免   

原文地址:https://www.cnblogs.com/V587h/p/13852015.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!