(一)、线程的基础
1. 什么是进程?
正在运行中的应用程序QQ,通常称为进程。每个进程都有自己独立的地址空间(内存空间),每当用户启动一个进程时,操作系统就会为该进程分配一个独立的内存空间,让应用程序在这个独立的内存空间中运行。
2.什么是线程?
线程是一个轻量级的子进程,是最小的处理单元;是一个单独的执行路径。可以说:线程是进程的子集(部分)。
3.线程特点:
线程是独立的。如果在一个线程中发生异常,则不会影响其他线程。它使用共享内存区域。
4.进程和线程的区别:
1、容易创建新线程。但是,创建新进程需要重复父进程。
2线程可以控制同一进程的其他线程**。进程无法控制兄弟进程,只能控制其子进程。
3、进程拥有自己的内存空间。线程使用进程的内存空间,且要和该进程的其他线程共享这个空间;而不是在进程中给每个线程单独划分一点空间。
4、(同一进程中的)线程在共享内存空间中运行,而进程在不同的内存空间中运行。
5、线程可以使用wait(),notify(),notifyAll()等方法直接与其他线程(同一进程)通信;而,进程需要使用“进程间通信”(IPC)来与操作系统中的其他进程通信。
5.多线程和线程:
多线程是指在系统中同时运行多个线程,如果只有一个处理器只有一个进程的一个指令被执行,可以使这些线程进行交替执行,由于间隔的时间短,这些程序看上去好像在同时运行.
如果将进程划分为线程,每个线程轮流占用处理器,可以减少并发时间.
6.线程、进程、多线程总结
(1).线程就是独立的执行路径;
(每个线程的路都有自己的路要走)
(2).在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,
gc线程
(3).main()称之为主线程,为系统的入口,用于执行整个程序。
(4).在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度,
调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
(5).对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
(6).线程会带来额外的开销,如cpu调度时间,并发控制开销。
(7).每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
(二)、线程创建 (3种)
7.Thread (线程创建)
基本方法:
1.首先要继承一个Thread
2.要重写run()方法
3.利用类调用start()开启线程
7.1Thread(获取url操作)
1.导入 commons jar包
2.调用commos jar包的copyurltofile 方法是下载指定路径的操作
FileUtils .copyURLToFile(new URL(url),new File(name));
3.设置多线程
import org.apache.commons.io.FileUtils;
import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
import java.io.File;
import java.net.URL;
public class Demo1 extends Thread {
private String url;
private String name;
private Demo1(String url, String name){
this.name=name;
this.url=url;
}
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
try {
webDownloader.downloader(url,name);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("下载名为:"+name);
}
//1.练习Thread,实现多线程同步下载图片
public static void main(String[] args) {
Demo1 demo1=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","1.jpg");
Demo1 demo1_two=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","2.jpg");
Demo1 demo1_three=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","3.jpg");
demo1.start();
demo1_two.start();
demo1_three.start();
}
}
//1.下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name) throws Exception{
FileUtils .copyURLToFile(new URL(url),new File(name));
}
}
8.Runnable (线程创建)
1.创建一个接口 Runnable
2.创建run()接口
3.在实现类创建一个接口类的对象
4.把接口类的对象放进Thread中
5.开启start()方法
public class Demo3 implements Runnable{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("run"+i);
}
}
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
System.out.println("main"+i);
}
//创建Runnable接口类的对象
Demo3 demo3=new Demo3();
//把类对象放进Thread中区
// Thread thread=new Thread(demo3);
// thread.start();
new Thread(new Demo3()).start();
}
}
9.Callable (线程创建)
1.实现callable 接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建执行服务
ExecutorService executorService= Executors.newFixedThreadPool(1);
4.提交执行:
Future<Boolean> future=executorService.submit(demo5);
5.获取结果
boolean b=future.get();
6.关闭服务
executorService.shutdown();
9.1基本语法
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo5 implements Callable<Boolean> {
@Override
public Boolean call() {
for (int i = 0; i <=1000 ; i++) {
System.out.println("call "+i +"次");
}
return true;
}
public static void main(String[] args) throws Exception{
for (int i = 0; i <=1000 ; i++) {
System.out.println("main"+" "+i);
}
Demo5 demo5=new Demo5();
//创建执行服务
ExecutorService executorService= Executors.newFixedThreadPool(1);
//提交执行:
Future<Boolean> future=executorService.submit(demo5);
//获取结果
boolean b=future.get();
//关闭服务
executorService.shutdown();
System.out.println(b);
}
}
9.2Callable(获取url)
import org.apache.commons.io.FileUtils;
import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
import java.io.File;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo1 implements Callable<Boolean> {
private String url;
private String name;
private Demo1(String url, String name){
this.name=name;
this.url=url;
}
//1.练习Thread,实现多线程同步下载图片
public static void main(String[] args) throws Exception{
Demo1 demo1=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","1.jpg");
Demo1 demo1_two=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","2.jpg");
Demo1 demo1_three=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","3.jpg");
//创建执行服务
ExecutorService executorService= Executors.newFixedThreadPool(3);
//提交执行:
Future<Boolean> future=executorService.submit(demo1);
Future<Boolean> future_one=executorService.submit(demo1_two);
Future<Boolean> future_two=executorService.submit(demo1_three);
//获取结果
boolean b1=future.get();
boolean b2=future_one.get();
boolean b3=future_two.get();
//关闭服务
executorService.shutdown();
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader=new WebDownloader();
try {
webDownloader.downloader(url,name);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("下载名为:"+name);
return true;
}
}
//1.下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name) throws Exception{
FileUtils .copyURLToFile(new URL(url),new File(name));
}
}
10.初识并发的问题
当多个线程,同时操作一个资源的时候,会出现数据紊乱
eg: 数量增加,抢同一张票
public class Demo4 implements Runnable{
private int ticket;
public Demo4(){ticket=10;}
@Override
public void run() {
while (true){
if (ticket<=0){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了-----"+ticket--+"张票");
}
}
public static void main(String[] args) {
Demo4 demo4=new Demo4();
new Thread(demo4,"小明").start();
new Thread(demo4,"老师").start();
new Thread(demo4,"黄牛").start();
}
}
11.模拟龟兔赛跑(利用线程)
需求文档:
1.设置一个共同的赛道 (线程)
2.判断比赛是否结束 (是否到达一百步)
3.打印胜利者
4.龟兔赛跑开始
5.故事中是乌龟赢,兔子需要睡觉
6.终于,乌龟赢得了比赛
Thread.currentThread().getName() 获取当前的名字
public class Race implements Runnable{
private String Vector;
@Override
public void run() {
for (int i = 0; i <=100; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子")&&i%10==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 boolean gameOver(int step){
if(Vector!=null){
return true;
} else { //
if(step>=100){
Vector=Thread.currentThread().getName();
System.out.println("成功的是:"+Vector);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
public class Demo2 implements Runnable{
String Vector;
@Override
public void run() {
//利用标志符号进行暂停线程
for (int i = 0; i <=100; i++) {
//当兔子走的步数等于50的时候,并且当走的是兔子的时候
if(i==50&&Thread.currentThread().getName().equals("兔子")){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"走了 "+i);
//得出胜利者并且结束循环的操作
if(!stop(i)){
break;
}
}
}
//判断是否成功的方法
public boolean stop(int i){
if (Vector!=null){
return false;
}else {
if (i>=100){
Vector=Thread.currentThread().getName();
System.out.println("成功的是:"+Vector);
return false;
}
}
return true;
}
public static void main(String[] args) {
Demo2 demo2=new Demo2();
Thread thread=new Thread(demo2,"兔子");
Thread thread1=new Thread(demo2,"乌龟");
thread.start();
thread1.start();
}
}
(三)、Thread 深度刨析
自定义一个接口的方法:
interface 接口的名字{
void 接口的方法名();
}
eg:
interface Marry{
void marry();
}
12.静态代理:
1.什么是代理:就是帮助雇主完成某项任务
静态代理模式总结:
(1).真实对象要和代理对象实现同一个接口
(2).代理对象要代理真是对象
静态代理模式的优点:
(3).代理对象可以帮助真实对象做一些真实对象做不了的事情
(4).真实对象可以更加专注的只做一件事情
普通代理模式:
public class Demo6 {
public static void main(String[] args) {
You you=new You();
Wedding_company wedding_company=new Wedding_company(you);
wedding_company.marry();
}
//久旱逢甘露
//他乡遇故知
//洞烛花房夜
//金榜题名时
}
共同接口
interface Marry{
void marry();
}
真实对象
class You implements Marry{
@Override
public void marry() {
System.out.println("李老师结婚了,超开心!");
}
}
代理对象
class Wedding_company implements Marry{
private Marry target;
public Wedding_company(Marry target) {
this.target = target;
}
@Override
public void marry() {
before_marry();
target.marry();
after_marry();
}
public void before_marry(){
System.out.println("结婚之前布置场景");
}
public void after_marry(){
System.out.println("结婚之后收尾款");
}
}
多线程实现静态代理模式
//代理实现Runnable 接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我爱你!");
}
}).start();
new Wedding_company(new You()).marry();
public class Demo6 {
public static void main(String[] args) {
//代理实现Runnable 接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我爱你!");
}
}).start();
new Wedding_company(new You()).marry();
}
//久旱逢甘露
//他乡遇故知
//洞烛花房夜
//金榜题名时
}
interface Marry{
void marry();
}
//真实的你
class You implements Marry{
@Override
public void marry() {
System.out.println("李老师结婚了,超开心!");
}
}
//婚庆公司
class Wedding_company implements Marry{
private Marry target;
public Wedding_company(Marry target) {
this.target = target;
}
@Override
public void marry() {
before_marry();
target.marry();
after_marry();
}
public void before_marry(){
System.out.println("结婚之前布置场景");
}
public void after_marry(){
System.out.println("结婚之后收尾款");
}
}
13.Lamda表达式
(一)、什么是:
1.希腊字母表中排序第11位的字母: lambda
2.避免匿名内部类定义过多
3.其实质属于函数式编程的概念
(二)、为什么要使用lambda表达式?
1.避免匿名内部类定义过多
2.可以让你的代码看起来很简洁
3.去掉了一堆没有意义的代码,只留下核心的逻辑
(三)、函数式接口
函数式接口的定义:
1.任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口。
eg :
public interface Runnable(){
public abstract void run();
}
2.对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
lamdb基本格式:
接口类对象=抽象方法的()->{"表达式"};
抽象方法是无参的时候
接口类对象需要被实现。
1.外部类
2.静态内部类
3.局部内部类
4.匿名类
5.lamdbad
public class Demo7 {
//3.静态内部类:
static class ILike_two implements ILike{
@Override
public void lambda() {
System.out.println("I LIKE LAMBDA two!");
}
}
public static void main(String[] args) {
//创建一个接口类对象 以及实现类的对象+
ILike like=new ILike_one();
like.lambda();
like=new ILike_two();
like.lambda();
//4.局部内部类:
class ILike_three implements ILike{
@Override
public void lambda() {
System.out.println("I LIKE LAMBDA three!");
}
}
like=new ILike_three();
like.lambda();
//5.匿名内部类
like= new ILike() {
@Override
public void lambda() {
System.out.println("I LIKE LAMBDA four!");
}
};
like.lambda();
//6.用lambda简化
like=()->{
System.out.println("I LIKE LAMBDA five!");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike{
void lambda();
}
//2.实现类:
class ILike_one implements ILike{
@Override
public void lambda() {
System.out.println("I LIKE LAMBDA !");
}
}
抽象方法是有参的时候
public class Demo8 {
//2.静态内部类
static class Love_1 implements I_Love {
@Override
public void love(int a) {
System.out.println("i love you " + a);
}
public static void main(String[] args) {
I_Love i_love = new Love();
i_love.love(1);
i_love=new Love_1();
i_love.love(2);
//3.局部内部类
class Love_2 implements I_Love {
@Override
public void love(int a) {
System.out.println("i love you " + a);
}
}
i_love=new Love_2();
i_love.love(3);
//4.匿名内部类
i_love=new I_Love() {
@Override
public void love(int a) {
System.out.println("i love you " + a);
}
};
i_love.love(4);
//5.lamdba
i_love = (int a) -> {
System.out.println(" i love you " + a);
};
i_love.love(5);
//5.1 简化1(参数类型)
i_love = a-> {
System.out.println(" i love you " + a);
};
i_love.love(6);
//5.2 简化2(括号(单个参数))
i_love = (int a) -> {
System.out.println(" i love you " + a);
};
i_love.love(7);
//5.3 简化3(花括号(单行))
i_love = (int a) ->
System.out.println(" i love you " + a);
i_love.love(8);
}
}
}
interface I_Love {
void love(int a);
}
//外部类:
class Love implements I_Love {
@Override
public void love(int a) {
System.out.println("i love you " + a);
}
}
(四)、五大线程状态
14.状态顺序
15.线程方法
1.setPriority( int newOriority) 更改线程优先级
2.static void sleep (long mills) 在指定的毫秒内礼让数据
3.void join() 等待该进程终止
4.static void yield() 暂停当前正在执行的线程对象,并执行其他线程
5.void interrupt() 中断线程,别用这个方式
6.boole isAlive() 测试线程是否处于活动状态
15.1停止线程(false)
1.不推荐使用JDK提供的stop()、destory()方法
2.推荐线程自己停止下来
3.建议使用一个标志位进行终止变量,即flag=false;
public class Demo9 implements Runnable{
//1.设置一个标识位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run......thread "+i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
Demo9 demo9=new Demo9();
//1.开启线程
new Thread(demo9).start();
for (int i = 0; i <1000 ; i++) {
if (i==900){
//调用stop方法进行线程停止
demo9.stop();
System.out.println("线程已经停止");
}
System.out.println("main "+i);
}
}
}
15.2线程休眠(sleep)
1.sleep() 指定当前线程阻塞的毫秒数
2.sleep()存在异常interruptedException;
3.sleep时间达到后线程就进入就绪状态
4.sleep可以模拟网络延时,倒计时等 (放大问题的发生性)
5.每个对象都有一个锁,sleep不会释放锁
利用线程模拟倒计时的操作:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
public class Demo10 {
public static void main(String[] args) throws Exception {
// tendown();
//打印系统当前时间
Date start=new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(start)); //打印时间
start=new Date(System.currentTimeMillis()); //更新时间
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void tendown() throws Exception{
int num=10;
while (true){
Thread.sleep(1000);
System.out.println("倒计时:"+num--);
if(num<=0){
break;
}
}
}
}
15.3线程礼让(yield)
1.礼让线程,让当前正在执行的线程暂停,但不阻塞
2.将线程从运行状态转为就绪状态
3.让cpu重新调度,礼让不一定成功。
public class Demo11 {
public static void main(String[] args) {
my_Yield my_yield=new my_Yield();
new Thread(my_yield,"A").start();
new Thread(my_yield,"B").start();
}
}
class my_Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行开始");
Thread.yield(); //礼让
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
15.4线程强行执行(join)
1.join 合并线程,待此线程执行完成后,在执行其他的线程,其他线程阻塞
2.可以想象成vip
public class Demo12 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i=0;i<100;i++) {
System.out.println("线程VIP来了");
}
}
public static void main(String[] args) throws Exception {
Demo12 demo12=new Demo12();
Thread thread=new Thread(demo12);
thread.start();
//主线程
for (int i = 0; i < 1000; i++) {
if(i==200){
thread.join(); //插队
}
System.out.println("main"+i);
}
}
}
16.观测线程状态(Thread.stata)
详解:
like=()->{ };
Thread thread=new Thread(like);
thread.start();
public class Demo13 {
public static void main(String[] args) {
//利用lam 设置Thread 的抽象方法
Thread thread=new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("/");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//观察状态
Thread.State state=thread.getState();
System.out.println(state); //NEW
//观察
thread.start(); //启动线程
state=thread.getState(); //启动后i的状态
System.out.println(state); //RUN
while (state!=Thread.State.TERMINATED){ //只要程序不重质,就一直输出
try {
Thread.sleep(100);
state=thread.getState(); //更新状态
System.out.println(state); //输出状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
17.线程优先级(Priority)
1.Java提供一个线程调度器来监控程序中启动后进入就绪状态的多有线程,线程调度
器按照优先级决定因该调用哪个线程来执行.
2.线程的优先级用数字表示会 1-10;
3.使用以下方式改变或获取优先级
getPriority().setPriority(int xx)
优先级的设定建议放在start()调度前
优先级并不代表着一定先调用
public class Demo14 {
public static void main(String[] args) {
//1.查看主线程默认的优先级
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
MyPriority myPriority=new MyPriority();
Thread thread_one=new Thread(myPriority);
Thread thread_two=new Thread(myPriority);
Thread thread_three=new Thread(myPriority);
Thread thread_four=new Thread(myPriority);
Thread thread_five=new Thread(myPriority);
Thread thread_six=new Thread(myPriority);
//设置优先级
thread_two.setPriority(1);
thread_three.setPriority(4);
thread_four.setPriority(Thread.MAX_PRIORITY);
thread_five.setPriority(8);
thread_six.setPriority(9);
//线程开启
thread_one.start();
thread_two.start();
thread_three.start();
thread_four.start();
thread_five.start();
thread_six.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
}
(五)、并发(锁)
18.守护(daemon)线程
1.线程分为用户线程和守护线程
2.虚拟机必须确保用户线程执行完毕
3.虚拟机不用等待守护线程执行完毕
4.eg:后台垃圾回收
基本思路:
守护线程是一直运行,但当用户线程结束后,后台也就结束了
public class Demo15 {
public static void main(String[] args) {
God god=new God();
You_daemon you_daemon=new You_daemon();
Thread thread=new Thread(god);
thread.setDaemon(true); //默认是false表示用户线程,正常的都是用户线程
thread.start(); //守护线程开启
new Thread(new You_daemon()).start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝一只保佑着你!");
}
}
}
class You_daemon implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++) {
System.out.println("人类一生都要快乐!");
}
System.out.println("========goodbye world ========");
}
}
19.线程同步(队列+锁)
1.发生条件: 多个线程操作同一个资源
2.由于统一进程的多个线程共享一块储存空间,在带来方便的同时,也带来了访问
冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时假如(锁机制)
synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,
使用后释放锁即可.
3.一个线程持有锁会导致其他所有需要此锁的线程挂起
4.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时,
引起性能问题
5.如果一个优先级高的线程等待一个优先级低的线程释放锁们会导致优先级倒置,
引起性能问题。
19.1 线程三大不安全案列
买票的问题:
(1).线程不安全,有负数。
public class Sercity_one {
//不安全的买票
public static void main(String[] args) {
Buy_tickets buy_tickets=new Buy_tickets();
new Thread(buy_tickets,"你").start();
new Thread(buy_tickets,"我").start();
new Thread(buy_tickets,"他").start();
}
}
class Buy_tickets implements Runnable{
//初始化票
private int ticket=10;
boolean flag=true;
@Override
public void run() {
while (flag){
buy();
}
}//买票
public void buy(){
if(ticket<=0){
flag=false;
return;
}else {
//进行网络延迟,放大问题的发生性
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName()+"买到了"+ticket--);
}
}
}
银行取钱的问题:
假如两个人同时取同一笔钱,那么银行可能会变成负
public class Sercity_two {
public static void main(String[] args) {
//账户初始化
account account_two=new account(100,"结婚基金");
//线程类的初始化
Bank bank=new Bank(account_two,50,"新浪");
Bank bank_one=new Bank(account_two,100,"新娘");
bank.start();
bank_one.start();
}
}
//账户
class account {
int money; //存款
String name; //账户名字
public account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Bank extends Thread{
account account_one;
int get_money; //取了多少钱
int now_money; //现在手上还有多少钱
//利用构造器进行
public Bank(account account_one,int get_money,String name){
super(name);
this.account_one=account_one;
this.get_money=get_money;
}
//取钱操作
public void run(){
//判断有没有钱
if(account_one.money-get_money<0){
System.out.println(Thread.currentThread().getName()+"余额不足,取不出");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account_one.money=account_one.money-get_money;
//你手上的钱
now_money=now_money+get_money;
//卡内余额为:
System.out.println(account_one.name+"余额为:"+account_one.money);
System.out.println(Thread.currentThread().getName()+"手里的钱:"+now_money);
}
}
集合不安全
实际数目:会出现实际集合数目减少的问题
import java.util.ArrayList;
import java.util.List;
public class Sercity_three {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println(list.size());
}
}
20.同步方法/同步块
同步方法:
public synchroniz ed void method(int args){}
synchronized 方法控制对 对象 的访问,每个对象都有对应的一把锁,
每个synchronized方法都必须获得调用该方法的对象的锁才能执行,
否则线程会被阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,
后面被阻塞得线程才能获得这个锁,继续执行。
缺点:
如果加了一个synchroniz的方法,会影响执行效率,锁太多的话,会影响资源。
《锁的对象一定要是变化的对象》
同步块的格式:
synchronized (obj){}
1.obj称之为:同步监视器
2.obj: 可以是任何对象,但是推荐使用共享资源作为同步监视器
3.同步方法中无需指定同步监听器,因为同步方法的同步监视器就是this,就是
这个对象本身,或则是class【反射中讲解】
4.同步监视器的执行过程:
(1).第一个线程访问,锁定同步监视器,执行其中的代码
(2).第二个线程访问,发现同步监视器被锁定,无法访问
(3).第一个线程访问完毕,解说同步监视器
(4).第二个线程访问,发现同步监视器没有锁,然后锁定并访问
20.1解决线程三大问题
利用同步块进行买票
不会出现
public class Sercity_one {
//不安全的买票
public static void main(String[] args) {
Buy_tickets buy_tickets=new Buy_tickets();
new Thread(buy_tickets,"你").start();
new Thread(buy_tickets,"我").start();
new Thread(buy_tickets,"他").start();
}
}
class Buy_tickets implements Runnable{
//初始化票
private int ticket=10;
boolean flag=true;
@Override
public void run() {
while (flag){
//进行网络延迟,放大问题的发生性,
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
public synchronized void buy(){ //设置为同步方法.锁的是this也就是Buy_tickets
if(ticket<=0){
flag=false;
return;
}else {
//买票
System.out.println(Thread.currentThread().getName()+"买到了"+ticket--);
}
}
}
解决银行取钱的问题
public class Sercity_two {
public static void main(String[] args) {
//账户初始化
account account_two=new account(100,"结婚基金");
//线程类的初始化
Bank bank=new Bank(account_two,50,"新浪");
Bank bank_one=new Bank(account_two,100,"新娘");
bank.start();
bank_one.start();
}
}
//账户
class account {
int money; //存款
String name; //账户名字
public account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Bank extends Thread{
account account_one;
int get_money; //取了多少钱
int now_money; //现在手上还有多少钱
//利用构造器进行
public Bank(account account_one,int get_money,String name){
super(name);
this.account_one=account_one;
this.get_money=get_money;
}
//取钱操作
public void run(){
synchronized (account_one){
//判断有没有钱
if(account_one.money-get_money<0){
System.out.println(Thread.currentThread().getName()+"余额不足,取不出");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account_one.money=account_one.money-get_money;
//你手上的钱
now_money=now_money+get_money;
//卡内余额为:
System.out.println(account_one.name+"余额为:"+account_one.money);
System.out.println(Thread.currentThread().getName()+"手里的钱:"+now_money);
}
}
}
解决集合的问题
import java.util.ArrayList;
import java.util.List;
public class Sercity_three {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
21.死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行
,而导致两个或则多个线程都在等待对方释放资源,都停止执行的情形,某一个
同步块同时拥有"两个以上对象的锁"时,就可能会发生"死锁"的问题
(我想要车,你想要枪。于是一直等待对方玩完)
死锁: 一直处于锁死的状态
public class Dead_lock {
public static void main(String[] args) {
Markup markup=new Markup(1,"灰菇凉");
Markup markup1=new Markup(0,"白雪公主");
markup.start();
markup1.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Markup extends Thread{
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;
String person_name;
Markup(int choice,String person_name){
this.choice=choice;
this.person_name=person_name;
}
public void run(){
try {
markup_two();
} catch (Exception e) {
e.printStackTrace();
}
}
//化妆
private void markup_two() throws Exception{
if (choice==0){
synchronized (lipstick){ //获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得了口红锁");
Thread.sleep(1000);
synchronized (mirror){ //一秒钟后想要镜子
System.out.println(Thread.currentThread().getName()+"获得了镜子锁");
}
}
}else {
synchronized (mirror){ //获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得了镜子锁");
Thread.sleep(2000);
synchronized (lipstick){ //一秒钟后想要镜子
System.out.println(Thread.currentThread().getName()+"获得了想要镜子锁");
}
}
}
}
}
非死锁: 都能够拿到需要的东西
public class Dead_lock {
public static void main(String[] args) {
Markup markup=new Markup(1,"灰菇凉");
Markup markup1=new Markup(0,"白雪公主");
markup.start();
markup1.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Markup extends Thread{
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;
String person_name;
Markup(int choice,String person_name){
this.choice=choice;
this.person_name=person_name;
}
public void run(){
try {
markup_two();
} catch (Exception e) {
e.printStackTrace();
}
}
//化妆
private void markup_two() throws Exception{
if (choice==0){
synchronized (lipstick){ //获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得了口红锁");
Thread.sleep(1000);
} synchronized (mirror){ //一秒钟后想要镜子
System.out.println(Thread.currentThread().getName()+"获得了镜子锁");
}
}else {
synchronized (mirror){ //获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得了镜子锁");
Thread.sleep(2000);
}synchronized (lipstick){ //一秒钟后想要镜子
System.out.println(Thread.currentThread().getName()+"获得了想要镜子锁");
}
}
}
}
死锁避免的方法:
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用
2.请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放
3.不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
22.lock(锁)
ReentrantLock 类
加锁: lock();
取锁: unlock();
加锁和取锁要写在 try{}finally{}中
import java.util.concurrent.locks.ReentrantLock;
public class Test_lock {
public static void main(String[] args) {
test2 test_one=new test2();
new Thread(test_one).start();
new Thread(test_one).start();
new Thread(test_one).start();
}
}
class test2 implements Runnable{
int ticket=10;
//定义锁
private final ReentrantLock reentrantLock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
reentrantLock.lock(); //加锁
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticket--);
}else {
break;
}
} finally {
reentrantLock.unlock(); //解锁
}
}
}
}
(六)、线程协作
生产者和消费者模式:
1.这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和
消费者之间相互依赖,互为条件。
(1).对于生产者:没有产品,通知消费者等待,有产品,马上通知消费者消费
(2).对于消费者:在消费后,通知消费者已经结束消费,需要生产新的产品进行
消费
(3).在生产者和消费者问题中 synchron是不够的
2.解决线程之间通信问题的方法:
(1). wait() 表示线程一直等待,直到其他线程通知,与sleep()不同,会释放锁
(2).wait(long timeout) 指定等待的毫秒数
(3).notify() 唤醒一个等待状态的线程
(4).notifyAll() 唤醒同一个对象上所调用wait()方法的线程,优先级别高的线程
优先调度