标签:time write 提前 文件创建 下载器 f11 mss priority 生产者消费者模式
Java.Thread类
程序:指令和数据的有效集合,本身没有任何运行含有,是一个静态的概念
进程:执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位
线程:通常一个进程可以包含多个线程(由调度器安排调度,不可人为干预)一个进程至少有一个线程,线程是CPU调度和执行的单位
自定义线程类并继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
启动方式:子类对象.start()
注:run()方法只有主线程一条执行路径,start()方法有多条执行路径,线程交替执行
package com.zhou3.Thread;
public class TestThreadDemo01 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 2000; i++) {
System.out.println("我在看run"+i);
}
}
public static void main(String[] args) {
//线程
//创建线程对象
TestThreadDemo01 testThreadDemo01 = new TestThreadDemo01();
//调用start方法
testThreadDemo01.start();
//main方法线程,主线程
for (int i = 0; i < 2000; i++) {
System.out.println("我在学多线程"+i);
}
}
}
定义线程类实现Runnable接口
实现run()方法,编写线程执行体
创建Thread线程对象,通过start()方法启动线程
启动方式:传入目标对象+Thread对象.start()
推荐使用此方式:可以避免单继承,灵活方便,方便一个对象被多个线程使用
package com.zhou3.Thread;
/*
多个线程同时操作同一个对象
没有对单一对象进行操作控制,存在并发问题
*/
public class TestThread implements Runnable{
private int ticketNumber = 10;
@Override
public void run() {
while (true){
if(ticketNumber <=0){
break;
}
else {
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNumber--+"张票");
}
}
}
public static void main(String[] args) {
new Thread(new TestThread(),"小美").start();
new Thread(new TestThread(),"黄牛").start();
new Thread(new TestThread(),"懒羊羊").start();
}
}
package com.zhou3.Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
练习多线程,同步下载漂亮的图片
*/
public class TestThreadDemo02 implements Runnable{
//类属性
private String url;
private String name;
//有参构造函数
public TestThreadDemo02(String url,String name){
this.name = name;
this.url = url;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println(name + "已经下载");
}
}
//下载器
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方法出现问题");
}
}
//main线程实现 多线程对象执行
public static void main(String[] args) {
TestThreadDemo02 t1 = new TestThreadDemo02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2F201110%2F20%2F104212ds59m9rtqspt6tzt.jpg&refer=http%3A%2F%2Fattach.bbs.miui.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908610&t=8c2e3d5e4418b17dcdccf84593a7cfed","1.jpg");
TestThreadDemo02 t2 = new TestThreadDemo02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908800&t=e7ef5c1ee4be471886018bc7b88f5aee","2.jpg");
TestThreadDemo02 t3 = new TestThreadDemo02("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb.zol-img.com.cn%2Fdesk%2Fbizhi%2Fimage%2F1%2F1680x1050%2F1349289433496.jpg&refer=http%3A%2F%2Fb.zol-img.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908797&t=fbd6b61bf2cec0bfed9d6ef9e23ce412","3.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
package com.zhou3.Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
/*
使用Callable接口实现多线程
好处:1. 可以定义返回值 2. 可以抛出异常
*/
//多线程类
public class CallableDemo01 implements Callable<Boolean> {
//线程类属性
private String url;
private String fileName;
//线程类构造器
public CallableDemo01(String url,String fileName){
this.url = url;
this.fileName = fileName;
}
//多线程操作体
@Override
public Boolean call() throws Exception {
webDownloader webDownloader1 = new webDownloader();
webDownloader1.Downloader(url,fileName);
System.out.println(fileName + "已经下载完成");
return true;
}
}
//下载器类
class webDownloader{
public void Downloader(String url,String fileName){
try {
FileUtils.copyURLToFile(new URL(url),new File(fileName));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,URL或文件创建时出错");
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDemo01 c1 = new CallableDemo01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2F201110%2F20%2F104212ds59m9rtqspt6tzt.jpg&refer=http%3A%2F%2Fattach.bbs.miui.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908610&t=8c2e3d5e4418b17dcdccf84593a7cfed","1.jpg");
CallableDemo01 c2 = new CallableDemo01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908800&t=e7ef5c1ee4be471886018bc7b88f5aee","2.jpg");
CallableDemo01 c3 = new CallableDemo01("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb.zol-img.com.cn%2Fdesk%2Fbizhi%2Fimage%2F1%2F1680x1050%2F1349289433496.jpg&refer=http%3A%2F%2Fb.zol-img.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618908797&t=fbd6b61bf2cec0bfed9d6ef9e23ce412","3.jpg");
//创建执行服务
//创建线程池
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> result1 = ser.submit(c1);
Future<Boolean> result2 = ser.submit(c2);
Future<Boolean> result3 = ser.submit(c3);
//获取结果
boolean rc1 = result1.get();
boolean rc2 = result1.get();
boolean rc3 = result1.get();
//关闭服务
ser.shutdownNow();
}
}
λ是希腊字母表中排行第十一位的字母,英语名称位Lambda
Lambda表达式的出现是为了简化繁琐的表达式,可以用于匿名内部类定义过多。属于函数式编程
//格式
(paramss) -> expression[表达式]
(paramss) -> statement[语句]
(paramss) -> {statements}
函数式接口:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
public interface Runnable{
public abstract void run();
}
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
package com.zhou3.Thread.Lambda;
/*
推导Lambda表达式
*/
public class LambdaDemo01 {
//2.静态内部类
static class Like2 implements iLike{
@Override
public void Lambda() {
System.out.println("I Like Lambda2");
}
}
public static void main(String[] args) {
iLike like = new Like();
like.Lambda();
like = new Like2();
like.Lambda();
//3.局部内部类
class Like3 implements iLike{
@Override
public void Lambda() {
System.out.println("I Like Lambda3");
}
}
like = new Like3();
like.Lambda();
//4.匿名内部类
like = new iLike() {
@Override
public void Lambda() {
System.out.println("I Like Lambda4");
}
};
like.Lambda();
//5.Lambda表达式,是对匿名内部类的再简化
//Lambda表达式就是对程序一步步的简化得来的,由于函数式接口只有一个方法,所以匿名内部类的接口和方法部分就理所当然的被去掉了
like = () -> {
System.out.println("I Like Lambda5");
};
like.Lambda();
}
}
//定义一个函数式接口
interface iLike{
void Lambda();
}
//1.接口实现类
class Like implements iLike{
@Override
public void Lambda() {
System.out.println("I Like Lambda1");
}
}
调度:操作系统给线程分配时间片(就绪状态进入运行状态的方式)
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 让当前正在执行的线程体休眠指定的毫秒数 |
void join() | 等待该线程终止 |
static void yield() | 暂停正在执行的线程对象,状态退回到就绪状态,重写等待时间片分配 |
boolean isAlive() | 测试线程是否处于活动状态 |
//在主线程中通过公开方法修改标志位来停止线程
package com.zhou3.Thread.ThreadState;
public class StopTest implements Runnable{
//1. 设置全局标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("Thread time ==" + i++);
}
}
//2. 设置一个线程停止的公开方法
public void threadStop1(){
this.flag = false;
}
public static void main(String[] args) {
StopTest stopTest1 = new StopTest();
new Thread(stopTest1).start();
for (int i = 0; i < 100000; i++) {
System.out.println("main"+i);
if (i==90000){
//调用stop停止线程
stopTest1.threadStop1();
System.out.println("stop has Call");
}
}
}
}
模拟网络延时可以放大问题发生的可能性
通过线程休眠实现每秒打印异常当前时间
package com.zhou3.Thread.ThreadState;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CountDownTest {
public static void main(String[] args) {
Date date1 = new Date(System.currentTimeMillis());//获取当前系统时间
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//格式化时间并输出
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date1));
//刷新当前时间
date1 = new Date(System.currentTimeMillis());
}
}
}
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
package com.zhou3.Thread.ThreadState;
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"线程第"+i+"次提前执行");
}
}
public static void main(String[] args) {
//次线程
TestJoin testJoin1 = new TestJoin();
Thread thread1 = new Thread(testJoin1);
thread1.start();
//主线程
for (int i = 0; i < 1000; i++) {
if (i == 200){
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main"+i);
}
}
}
通过Thread.getState()方法来观测线程状态
package com.zhou3.Thread.ThreadState;
public class TestStateObservation {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 20; i++) {
System.out.println("此线程正在跑");
}
});
//new时状态观测
System.out.println(thread1.getState());
//start时状态观测
thread1.start();
Thread.State state1 = thread1.getState();
//循环输出该线程其他状态
while (state1 != Thread.State.TERMINATED){
Thread.sleep(100);
System.out.println(state1);
state1 = thread1.getState();//更新线程状态
}
}
}
线程优先级高不一定先执行,只是先执行的概率会大大提高(给的资源多)
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度那个线程来执行
线程优先级的表示
用数字来表示,范围1~10
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
改变线程优先级的方法
getPriority()
setPriority()
package com.zhou3.Thread.ThreadState;
public class TestPriority {
public static void main(String[] args) {
MyPriority myPriority1 = new MyPriority();
Thread thread1 = new Thread(myPriority1,"1");
Thread thread2 = new Thread(myPriority1,"2");
Thread thread3 = new Thread(myPriority1,"3");
Thread thread4 = new Thread(myPriority1,"4");
Thread thread5 = new Thread(myPriority1,"5");
thread1.start();
thread2.setPriority(1);
thread2.start();
thread3.setPriority(10);
thread3.start();
thread4.setPriority(8);
thread4.start();
thread5.setPriority(3);
thread5.start();
//主线程优先级,默认优先级
System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
}
}
并发:同一个对象被多个线程同时操作
现实生活中:我们遇到这种问题(食堂排队打饭)时,解决办法就是排队一个一个来
在处理多线程问题时,遇到这种并发情况,我们就需要线程同步。
线程同步:是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一次才开始使用
这是线程同步的形成条件
每一个对象都有一把锁,可以实现线程安全
锁机制:synchronized排他锁,可以独占资源,而其他线程必须等待锁的释放
存在的问题:
默认锁this
锁的对象需要是Object类型的
java.util.concurrent //并发包、这是一个有关并发编程的包
相互等待对方锁定的资源:自己抱着一个锁去等另外一个锁。想要同时持有两个锁,但是都不想给
死锁的避免(四个必要条件)
上面这四个必要条件,只要想办法破解其中任意一个或多个就可以避免死锁的发生
注意:锁要写在并非线程类中
注意:要区分线程循环和方法循环的区别
Lock是显式锁,synchronized是隐式锁
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且有更好的拓展性
优先顺序
Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步代码块(方法体之外)
自我总结:
Lock可以显式的对不安全代码进行加锁和解锁,适合在run方法中写代码的情况
package com.zhou3.Thread.Unsafe;
import java.util.concurrent.locks.ReentrantLock;
/*
lock锁测试
*/
public class TestLock {
public static void main(String[] args){
UnSafe safe1 = new UnSafe();
new Thread(safe1,"hong").start();
new Thread(safe1,"ming").start();
new Thread(safe1,"A").start();
}
}
class UnSafe implements Runnable {
private int ticketNumber = 10;
private final ReentrantLock lock =new ReentrantLock();
@Override
public void run(){
while (true){
try {
lock.lock();
if(ticketNumber <= 0){
return;
}
//sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"已经买到第"+ticketNumber+"张票");
ticketNumber--;
}finally{
lock.unlock();
}
}
}
// private synchronized void buyTicket(){
// for (int i = 0; i < 1000000; i++) {
// if(ticketNumber<=0){
// return;
// }
// //sleep
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"已经买到第"+i+"张票");
// ticketNumber--;
// }
// }
// private void buyTicket() {
// if(ticketNumber <= 0){
// flag = false;
// return;
// }
//
// //sleep
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName()+"已经买到第"+ticketNumber+"张票");
// ticketNumber--;
// }
}
synchronized关键字是隐形的对this加锁,可以使得本类一次只能一个线程调用。关键字的位置很重要,适合在多线程类中写方法,在run中调用方法
package com.zhou3.Thread.Demo01;
/*
多个线程同时操作同一个对象
*/
public class TestThread {
public static void main(String[] args) {
BuyTicket buyTicket1 = new BuyTicket();
new Thread(buyTicket1,"小美").start();
new Thread(buyTicket1,"黄牛").start();
new Thread(buyTicket1,"懒羊羊").start();
// new Thread(new BuyTicket(),"小美").start();
// new Thread(new BuyTicket(),"大美").start();
// new Thread(new BuyTicket(),"小明").start();
}
}
class BuyTicket implements Runnable{
boolean flag = true;
private int ticketNumber = 10;
@Override
public void run() {
while (flag){
// if(ticketNumber <=0){
// break;
// }
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
buy();
}
}
public synchronized void buy(){
if(ticketNumber <=0){
flag = false;
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNumber--+"张票");
}
}
生产者消费者模式(问题)
Java中提供的几个解决线程通信的问题
方法名 | 作用 |
---|---|
wait() | 线程会一直等待,直到其他线程通知,同sleep不同会释放锁 |
wait(long timeout) | 等待指定毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度 |
注:以上方法都是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException。
通过缓冲区过度数据
Producer(生产者) 缓冲区 Consumer(消费者)
package com.zhou3.Thread.ThreadCommunication;
/*
测试生产者消费者模型,管程法
生产者,消费者,产品,缓冲区
*/
public class TestPC {
public static void main(String[] args){
SynContainer container1 = new SynContainer();
Producer producer1 = new Producer(container1);
Consumer consumer1 = new Consumer(container1);
new Thread(producer1,"工具员").start();
new Thread(consumer1,"黑心狗").start();
}
}
class Producer implements Runnable{
SynContainer synContainer1;
//构造器
public Producer(SynContainer synContainer1){
this.synContainer1 = synContainer1;
}
@Override
public void run(){
for (int i = 0; i < 200; i++) {
synContainer1.push(new Code(i));
System.out.println("生产代码数"+i);
}
}
}
class Consumer implements Runnable{
SynContainer synContainer2;
//构造器
public Consumer(SynContainer synContainer2){
this.synContainer2 = synContainer2;
}
@Override
public void run(){
for (int i = 0; i < 200; i++) {
Code code1 = synContainer2.pop();
System.out.println("消费代码数"+i);
}
}
}
class Code{
public int codeNumber;
public Code(int codeNumber){
this.codeNumber = codeNumber;
}
}
class SynContainer{
//容量大小
Code[] codes = new Code[20];
int count = 0;
//生产者放入
public synchronized void push(Code code){
//容器满,等待消费
if(count == codes.length){
System.out.println("容器已满");
//发送通知
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有满,丢入
codes[count] = code;
count++;
//发送通知,生产完再通知,不然没用
this.notifyAll();
}
//消费者拿走
public synchronized Code pop(){
//产品不足,等待
if(count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//产品足够,正常运行
count--;
Code code = codes[count];
//通知生产,吃完再通知,不然通知到了还是满的
this.notifyAll();
return code;
}
}
注意:一个文件中只能有一个public类
相当于是只有一个位置的缓冲区
注意标志位每次调用方法都要更改
package com.zhou3.Thread.ThreadCommunication;
/*
使用信号灯法,实现传小纸条聊天
我,对象,内容
*/
public class TestLight {
public static void main(String[] args) {
Massage massage = new Massage();
new Thread(new My(massage),"my").start();
new Thread(new Zx(massage),"zx").start();
System.out.println("我们会好好的");
}
}
class My implements Runnable{
Massage massage = new Massage();
//我要用这个蓝色的小纸条
public My(Massage massage){
this.massage = massage;
}
//我这次打算写100条“我喜欢你”到小纸条上
@Override
public void run() {
for (int i = 0; i < 100; i++) {
massage.writeMassage("我好喜欢你呀!");
}
}
}
class Zx implements Runnable{
//我要听你说十次我喜欢你
Massage massage1 = new Massage();
//用这个纸条我知道了
public Zx(Massage massage){
this.massage1 = massage;
}
@Override
public void run() {
//我要听100此你喜欢我
for (int i = 0; i < 100; i++) {
String love = massage1.readMassage();
System.out.println(Thread.currentThread().getName()+"听到你对我说:"+love);
}
}
}
class Massage{
String mag;
boolean flag = true;
//写小纸条
//往小纸条上写东西
public synchronized void writeMassage(String mag){
if(flag != true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.mag =mag;
System.out.println(Thread.currentThread().getName()+"往小纸条上写了:"+mag);
this.flag = !flag;
//写好了,你可以看了
this.notifyAll();
}
//看小纸条方法
public synchronized String readMassage(){
//如果没有写好,那我就等等
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//我要看了
System.out.println("我看了小纸条");
//我看完了,你再写呀!
this.notifyAll();
this.flag = !this.flag;
return this.mag;
}
}
从JDK5.0起提供了线程池相关的API:ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)//执行任务,没有返回值,用来执行Runnable
<T> Future<T> submit(Callable<T> task)//有返回值,一般用来执行Callable
void shutdown()//关闭线程池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package com.zhou3.Thread.TestPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPoolRunnable {
public static void main(String[] args) {
//创建线程池
MyThread myThread1 = new MyThread();
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(myThread1);
service.execute(myThread1);
service.execute(myThread1);
service.execute(myThread1);
service.shutdownNow();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
标签:time write 提前 文件创建 下载器 f11 mss priority 生产者消费者模式
原文地址:https://www.cnblogs.com/zhoushuaiyi/p/14591925.html