Thread线程
创建线程方式一:继承Thread类,重写run()方法,调用start开启线程 (start是两条路 同时执行)
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
public class TestThread01 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 10; i++) {
System.out.println("学习Java--"+i);
}
}
public static void main(String[] args) {
//main线程
//创建一个线程对象
TestThread01 testThread01 = new TestThread01();
//调用start()方法开启线程
testThread01.start();
for (int i = 0; i < 10; i++) {
System.out.println("多线程"+i);
}
}
}
创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
启动线程:传入目标对象 + Thread对象.start()
不建议使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
public class TestThread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在写代码---"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread03 testThread03 = new TestThread03();
//创建线程对象,通过线程对象来开启我们的线程,代理
// Thread thread = new Thread(testThread03);
//
// thread.start();
new Thread(testThread03).start();
for (int i = 0; i < 10; i++) {
System.out.println("学习多线程---"+i);
}
}
}
创建线程方式三:实现callable接口
好处:1. 可以定义返回值;2. 可以抛出异常
//创建线程方式三:实现callable接口
public class TestCallable implements Callable<Boolean> {
private String url; //地址
private String name; //文件名
public TestCallable(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 {
TestCallable t1 = new TestCallable("https://uploadstatic.mihoyo.com/puzzle/upload/puzzle/2022/08/12/f8f8b39b79082490dc7a51cb58c033a4_1886787457893712308.jpg?x-oss-process=image/format,webp/quality,Q_90","1.jpg");
TestCallable t2 = new TestCallable("https://uploadstatic.mihoyo.com/puzzle/upload/puzzle/2022/08/12/f8f8b39b79082490dc7a51cb58c033a4_1886787457893712308.jpg?x-oss-process=image/format,webp/quality,Q_90","2.jpg");
TestCallable t3 = new TestCallable("https://uploadstatic.mihoyo.com/puzzle/upload/puzzle/2022/08/12/f8f8b39b79082490dc7a51cb58c033a4_1886787457893712308.jpg?x-oss-process=image/format,webp/quality,Q_90","3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交服务
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//关闭服服务
ser.shutdownNow();
}
}
//下载器
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方法出现问题");
}
}
}
总结:线程开启不一定立即执行,由CPU调度执行
Lamdba表达式
public class TestLamdba2 {
public static void main(String[] args) {
//ILove love = null;
//1. lamdba简化
ILove love = (int a)->{
System.out.println("I love you-->"+a);
};
//简化1. 参数类型
love = (a)->{
System.out.println("I love you-->"+a);
};
//简化2. 简化括号
love = a->{
System.out.println("I love you-->"+a);
};
//简化3. 去掉大括号
love = a -> System.out.println("I love you-->"+a);
love.love(0);
}
}
//定义一个函数式接口
interface ILove{
void love(int a);
}
总结:
-
lamdba表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。
-
前提是接口为函数式接口
-
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
静态代理模式
public class StacticProxy {
public static void main(String[] args) {
new Thread(()-> System.out.println("520")).start();
// WeddingCompany weddingCompany = new WeddingCompany(new You());
// weddingCompany.HappyMarry();
new WeddingCompany(new You()).HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("我抽到钟离,超开心");
}
}
//代理角色
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before() {
System.out.println("原石");
}
private void after() {
System.out.println("都没了");
}
}
总结:
-
真实对象和代理对象都要实现同一个接口
-
代理对象要代理真实对象
好处:
-
代理对象可以做很多真实对象做不了的事件
-
真实对象专注做自己的事情
线程停止
public class TestStop implements Runnable{
//1. 设置一个标识位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run.....Thread" + i++);
}
}
//2. 设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 100; i++) {
System.out.println("main-->"+i);
if (i == 90){
testStop.stop();
System.out.println("线程停止");
}
}
}
}
建议:
-
建议线程正常停止--->利用次数,不建议死循环
-
建议使用标志位--->设置一个标志位
-
不要使用stop或者destroy等过时或者JDK不建议使用的方法
线程礼让—Yield
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程优先级
-
线程的优先级用数字表示,范围从1~10
-
Thread.MIN_PRIORITY = 1;
-
Thread.MAX_PRIORITY = 10;
-
Thread.NORM_PRIORITY = 5;
-
-
使用以下方法改变或获取优先级
-
getPriority()
-
setPriority(int XXX)
-
注意:优先级的设定建议在start()调度前
同步块
-
同步块:synchronized ( Obj ) { }
-
Obj称之为同步监视器
-
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
-
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
-
-
同步监视器的执行过程
-
第一个线程访问,锁定同步监视器,执行其中代码.
-
第二个线程访问,发现同步监视器被锁定,无法访问.
-
第一个线程访问完毕,解锁同步监视器.
-
第二个线程访问,发现同步监视器没有锁,然后锁定并访问
-
线程通信(生产者与消费者问题)
解决方式1:管程法
//测试:生产者消费者模型-->利用缓冲区解决:管程法
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生成了"+i+"原石");
container.push(new Chicken(i));
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->"+container.pop().id+"原石");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
while (count == 10){
try{
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
while (count == 0){
try{
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
}
}
解决方式2:信号灯法
//测试:生产者消费者模型-->利用标志位解决:信号灯法
public class TestPC2 {
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 < 10; i++) {
if (i%2==0){
this.tv.play("快乐大本营");
}else {
this.tv.play("抖音");
}
}
}
}
//消费者--观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
tv.watch();
}
}
}
//产品--节目
class TV{
//演员表演,观众等待 T
//观众观看,演员等待 F
String voice; //表演的节目
boolean flag = true;
//表演
public synchronized void play(String voice){
if (!flag){
this.watch();
}
System.out.println("演员表演了:"+voice);
//通知观众观看
this.notifyAll(); //通知唤醒
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag = ! this.flag;
}
}