多线程01:概述
多线程02:线程,进程,多线程
- Process:进程
- THread:线程
多线程03:继承Thread类
- 多条线程是同时运行的(交替执行)
package Base;
//创建线程的方式一:继承Thread类,重写run()方法,创建线程对象调用start开启线程
//总结:线程开启不一定立即执行,由CPU调度
public class TestThread1 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println(i+"我在写代码");
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程,,,不能调用run方法
testThread1.start();
for (int i = 0; i < 2000; i++) {
System.out.println(i+"我在学习多线程");
}
}
}
多线程03:网图下载
package Base;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread,实现多线程同步下载图片
public class TestThread1 extends Thread {
private String url; //网络图片地址
private String name;//保存的文件名
public TestThread1(String url, String name) {
this.url = url;
this.name = name;
}
//下载图片的线程执行体
@Override
public void run() {
//使用下载器
webDownLoader webDownLoader = new webDownLoader();
webDownLoader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}
//主线程
public static void main(String[] args) {
//使用构造方法传入图片地址和保存图片的名称
TestThread1 t1 = new TestThread1("https://img-blog.csdnimg.cn/20210514125715463.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MzMzMTk0Mg==,size_16,color_FFFFFF,t_70","2.jpg");
//多个线程是同时执行的
t1.start();
}
}
//首先写一个下载器
class webDownLoader{
//下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现问题");
}
}
}
实现Runnable的接口
package Base;
public class Demo2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(i+"我在学习java");
}
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
new Thread(demo2).start();
for (int i = 0; i < 1000; i++) {
System.out.println("java真香");
}
}
}
- 对比小结
多线程06:初识并发问题
package Base;
//多个线程同时操作同一个对象
//买火车票的例子
public class Demo2 implements Runnable{
private int ticket = 10;
@Override
public void run() {
while(true){
if (ticket<=0){
break;
}
ticket--;
//通过Thread.currentThread().getName()方法获得当前线程的名字
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket+"票");
}
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
new Thread(demo2,"王姐").start();
new Thread(demo2,"li姐").start();
new Thread(demo2,"zhang姐").start();
}
}
- 发现问题:多个线程操作一个资源时。线程不安全,数据紊乱
多线程07:乌兔赛跑
package Base;
public class Demo2 implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//如果比赛结束就停止比赛
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
//判断比赛是否完成
public boolean gameOver(int steps){
if (winner!=null){ //已经存在胜利者了
return true;
}
if (steps>=100){
winner=Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
return false;
}
public static void main(String[] args) {
//创建一个赛道
Demo2 demo2 = new Demo2();
new Thread(demo2,"乌龟").start();
new Thread(demo2,"兔子").start();
}
}
多线程08:实现Callable接口(了解即可)
- 改进下载网图
package Base;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//练习Thread,实现多线程同步下载图片
class Demo3 implements Callable<Boolean> {
private String url; //网络图片地址
private String name;//保存的文件名
public Demo3(String url, String name) {
this.url = url;
this.name = name;
}
//下载图片的线程执行体
@Override
public Boolean call() {
//使用下载器
webDownLoader webDownLoader = new webDownLoader();
webDownLoader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
//主线程
public static void main(String[] args) throws ExecutionException, InterruptedException {
//使用构造方法传入图片地址和保存图片的名称
Demo3 t1 = new Demo3("https://img-blog.csdnimg.cn/20210514125715463.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MzMzMTk0Mg==,size_16,color_FFFFFF,t_70","2.jpg");
//多个线程是同时执行的
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> r1 = ser.submit(t1);
boolean r = r1.get();
System.out.println(r);
ser.shutdownNow();
}
}
//首先写一个下载器
class webDownLoader1{
//下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现问题");
}
}
}
多线程09:静态代理模式
package Base;
/**
* 静态代理模式总结
* 真实对象和代理对象都要实现一个共同的接口
* 代理对象要代理真实角色
* 好处:
* 代理对象可以做很多真实对象做不了的事情
* 真实对象专注做自己的事情
*/
public class Demo3 {
public static void main(String[] args) {
WeddingCCompany weddingCCompany = new WeddingCCompany(new You());
weddingCCompany.happyMarry();
}
}
//共同的接口
interface marry{
void happyMarry();
}
//真是角色 你去结婚
class You implements marry{
@Override
public void happyMarry() {
System.out.println("woyaojiehunle gaoshuwoaini ");
}
}
//代理角色 帮助你结婚
class WeddingCCompany implements marry{
private marry target;
public WeddingCCompany(marry target) {
this.target = target;
}
@Override
public void happyMarry() {
after();
this.target.happyMarry();
before();
}
private void after() {
System.out.println("xingfu");
}
private void before() {
System.out.println("tongku");
}
}
多线程10:Lamada表达式
package Base;
public class Demo3 {
public static void main(String[] args) {
// lambda表达式
test p = (int a) ->{System.out.println("I Love---"+a); };
//简化1.去掉参数类型
test p = (a) ->{System.out.println("I Love---"+a); };
p.love(520); // I Love---520
//简化2.去掉参数类型和()
test p = a ->{System.out.println("I Love---"+a); };
p.love(521); //I Love---521
//简化3.去掉参数类型和()和{}
//注意:lambda表达式只能有一行代码的情况下才能成为一行,如果有多行,则使用代码块({})包裹;
// 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
//前提:接口必须是函数式接口(一个接口中只有一个方法)
test p = a ->System.out.println("I Love---"+a);
p.love(521); //I Love---521
}
}
interface test{
void love(int a);
}
多线程11:线程停止
package Base;
//测试stop
//1.建议线程正常停止---> 利用次数 ,不建议死循环
//2.建议使用标志位--->这只一个标志位
//3.不要使用stop或者destroy等过的或者JDK不建议的使用方法
public class Demo3 implements Runnable {
//设置一个标志位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
//在内部进行i++也可以
System.out.println("run -------Thread"+i++);
}
}
//设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
new Thread(demo3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main "+i);
if (i==900){
//调用stop方法,切换标志位,停止线程
demo3.stop();
System.out.println("该线程停止");
}
}
}
}
多线程12:线程休眠—sleep
- 模拟网络延时
package Base;
//多个线程同时操作同一个对象
//买火车票的例子
public class Demo3 implements Runnable{
private int ticket = 10;
@Override
public void run() {
while(true){
if (ticket<=0){
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
//通过Thread.currentThread().getName()方法获得当前线程的名字
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket+"票");
}
}
public static void main(String[] args) {
Demo3 demo2 = new Demo3();
new Thread(demo2,"王姐").start();
new Thread(demo2,"li姐").start();
new Thread(demo2,"zhang姐").start();
}
}
- 模拟倒计时
//模拟倒计时
public static void ten() throws InterruptedException {
int t = 10;
while(true){
Thread.sleep(1000);
System.out.println(t);
t--;
if (t<0){
break;
}
}
}
}
- 打印系统当前时间
public static void main(String[] args) {
//打印当前系统时间
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();
}
}
}
多线程13:线程礼让——yield
public class Demo3 {
public static void main(String[] args) {
MyYield m1 = new MyYield();
new Thread(m1,"a").start();
new Thread(m1,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield(); //线程礼让
System.out.println(Thread.currentThread().getName()+"线程执行完毕");
}
}
a线程开始执行
b线程开始执行
a线程执行完毕
b线程执行完毕
- 礼让不一定成功,看CPU心情
多线程14:线程强制礼让——join
- 注意调用方法
package Base;
public class Demo3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程Vip来了 "+i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo3 demo3 = new Demo3();
Thread thread = new Thread(demo3, "aa");
thread.start();
for (int i = 0; i < 500; i++) {
if (i==200){
thread.join();
}
System.out.println("main "+i);
}
}
}
多线程15:观测线程状态
package Base;
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///");
});
//观察转台
Thread.State state = thread.getState();
System.out.println(state);
//观察启动状态
thread.start();
state = thread.getState(); //不用每次都创建一个新的对象
System.out.println(state);
while(state!=Thread.State.TERMINATED){//只要线程不终止,就会一直输出
Thread.sleep(1000);
state =thread.getState();
System.out.println(state);
}
}
}
NEW
RUNNABLE
RUNNABLE
RUNNABLE
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
///
TERMINATED
注意:lambda表达式的使用处
注意:线程结束后就不能再start()调用了
多线程15:线程的优先级(priority)
- 注意:优先级高的不一定先跑,但是大多数情况是这样
- 注意:优先级的设定建议在start()前调度
package Base;
public class Demo3 {
public static void main(String[] args) {
//主线程默认优先级
//使用Thread.currentThread().getPriority()来获取本身的优先级
System.out.println(Thread.currentThread().getName() + "--->" + Thread.currentThread().getPriority());
bbb bbb = new bbb();
Thread t1 = new Thread(bbb, "lhao");
//先设置优先级在启动
Thread t2 = new Thread(bbb, "lihao");
t2.setPriority(Thread.MAX_PRIORITY);
t2.start();
Thread t5 = new Thread(bbb, "lao");
t5.setPriority(4);
t5.start();
Thread t4 = new Thread(bbb, "lao");
t4.setPriority(6);
t4.start();
}
}
class bbb implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"---->"+Thread.currentThread().getPriority());
}
}
多线程17:守护线程(daemon)
- 虚拟机不用管守护线程执没执行完,只管用户线程执行完毕就行
- 虚拟机停止也需要一段时间
- 使用 thread.setDaemon(true); 来设置守护线程
package Base;
public class Demo3 {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
//使用 thread.setDaemon来设置守护线程
thread.setDaemon(true); //默认是false表示用户线程, 正常的线程都是用户线程
thread.start(); // 守护线程启动
new Thread(you).start();//用户线程启动
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("我的一生很快乐");
}
System.out.println("goodbye-----world");
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝保护着我");
}
}
}
多线程18:线程同步机制
多线程19:三大不安全的案例
注意:使用Thread.sleep来放大问题的发生性
- 不安全的买票----有负数
package Base;
public class Demo3 {
public static void main(String[] args) {
BuyTicket st = new BuyTicket();
Thread t1 = new Thread(st,"张三");
Thread t2 = new Thread(st,"李四");
Thread t3 = new Thread(st,"王五");
t1.start();
t2.start();
t3.start();
}
}
class BuyTicket implements Runnable{
//票
private int ticket = 10;
//设置外部停止标志
boolean flag = true;
@Override
public void run() {
//买票
while (flag){
test();
}
}
//买票的方法
public void test() {
//判断是否有票
if (ticket>10||ticket<=0){
System.out.println("对不起,票已经售空");
flag =false;
}
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+ticket--);
}
}
- 不安全的取钱
package Base;
public class Demo3 {
public static void main(String[] args) {
//账户
Account account = new Account(1000000,"结婚基金");
Drawing you = new Drawing(account,500000,"你");
Drawing girlFriend = new Drawing(account,1000000,"GirlGood");
you.start();
girlFriend.start();
}
}
//账户
class Account{
int money; //余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;//账户
//取了多少钱
int drawingMoney;
//手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
//调用父类的构造方法
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
//判断有没有钱
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,去不了");
return;
}
//模拟延时 sleep可以放大问题的发生行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡上的钱
account.money = account.money-drawingMoney;
//你手里的钱
nowMoney = drawingMoney +nowMoney;
System.out.println(account.name+"余额为:"+account.money);
//此时Thread.currentThread().getName() = this.getName()
System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);
}
}
结婚基金余额为:-500000
结婚基金余额为:-500000
你手里的钱500000
GirlGood手里的钱1000000
- 线程不安全的集合
package Base;
import java.util.ArrayList;
import java.util.List;
public class aaa {
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());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果为:9998
因为:两个线程同一瞬间操作了同一位置,添加到了同一个位置,覆盖掉了,元素就少了
多线程20:同步方法及同步块
- synchronized相当于一个修饰符 ,修饰方法变为===>同步方法
-
加上synchronize就相当于排队 上锁 不能抢
-
synchronize方法锁的是类本身
-
synchronize同步块的锁的是任何对象,锁的对象为变化的对象(增删改),块中为你想要执行的语句
//买票的方法
public synchronized void test() {
//判断是否有票
if (ticket>10||ticket<=0){
System.out.println("对不起,票已经售空");
flag =false;
return;
}
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+ticket--);
}
}
在买票的方法上加了synchronize,变为同步方法(上锁了),
锁的是方法所在的类的对象(本身)
上面取钱的例子:如果锁的是取钱的方法实际上锁的是该方法所在的类对象--银行,
但是银行没有变化(增删改),所以我们要锁变化的对象(账户),
所以使用同步块然后传入账户的类对象;
//取钱
@Override
public void run() {
synchronized (account){
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,去不了");
return;
}
//模拟延时 sleep可以放大问题的发生行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡上的钱
account.money = account.money-drawingMoney;
//你手里的钱
nowMoney = drawingMoney +nowMoney;
System.out.println(account.name+"余额为:"+account.money);
//此时Thread.currentThread().getName() = this.getName()
System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);
}
结婚基金余额为:500000
你手里的钱500000
GirlGood钱不够,去不了
package Base;
import java.util.ArrayList;
import java.util.List;
public class aaa {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(list.size());
}
}
结果为:100
多线程21:CopyOnWriteArrayList
- JUC下的一个线程安全的集合(copyonwritearraylist)
package Base;
import java.util.concurrent.CopyOnWriteArrayList;
public class aaa {
public static void main(String[] args) {
CopyOnWriteArrayList<String> c = new CopyOnWriteArrayList();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
c.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(c.size());
}
}
不用同步快也可以输出1000,
因为CopyOnWriteArrayList是他人写好的线程安全的集合
多线程22:死锁 (多个线程互相抱着对方资源,然后形成僵持)
package Base;
//死锁:多个线程互相抱着对方资源,然后形成僵持
public class aaa {
public static void main(String[] args) {
makeUp g1 = new makeUp(0,"灰姑娘");
makeUp g2 = new makeUp(1,"白雪公主");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror {
}
//化妆
class makeUp extends Thread{
//需要的资源只有一份,用static来修饰保证只有一份,用static修饰的只有一份
static Lipstick l = new Lipstick();
static Mirror m = new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
//构造方法好赋值
public makeUp( int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
//线程中调用化妆的方法
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆的方法
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (l){
System.out.println(this.girlName+"拿到了口红的锁");
Thread.sleep(1000);
synchronized (m){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}else{
synchronized (m){
System.out.println(this.girlName+"拿到了镜子的锁");
Thread.sleep(2000);
synchronized (l){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
被卡死了:
灰姑娘拿到了口红的锁
白雪公主拿到了镜子的锁
- 所以需要将同步块中的两个对象锁变为一个就不会出现死锁的问题了、
- 用static修饰的变量(资源)只有一份
Lock锁–接口
package Base;
import java.util.concurrent.locks.ReentrantLock;
public class bbb {
public static void main(String[] args) {
TestLock t1 = new TestLock();
TestLock t2 = new TestLock();
TestLock t3 = new TestLock();
t1.start();
t2.start();
t3.start();
}
}
class TestLock extends Thread{
private int ticket =10;
//定义锁
ReentrantLock lock =new ReentrantLock();
@Override
public void run() {
while(true){
try{
//显示加锁
lock.lock();
if(ticket>0){
System.out.println(ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("票没了");
break;
}
}
finally {
//解锁
lock.unlock();
}
}
}
}
- 使用的是Lock的实现类ReentrantLock
- 需要使用try–finally
- Lock是一个接口
多线程24:生产者消费者问题
设置标志位
多线程24:信号灯法
package Base;
//测试:信号灯法,标志位
//生产者,消费者,产品,缓冲区
public class bbb {
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("kuaile da");
} else {
this.tv.play("douyin ");
}
}
}
}
class Watcher extends Thread{
Tv tv;
public Watcher(Tv tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
tv.watch();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
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() throws InterruptedException {
if (flag){
this.wait();
}
System.out.println("观看了"+voice);
this.notifyAll();
this.flag =!this.flag;
}
}
线程池
package Base;
//测试:信号灯法,标志位
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class bbb {
public static void main(String[] args) {
//1.创建服务,创建线程池 规定线程池的数量
ExecutorService service = Executors.newFixedThreadPool(10);
//2,执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//3.关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
结果:
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-4