1.创建线程的方法
1.1通过继承Thread类创建线程
通过继承Thread类来创建线程的一般流程:继承Thread类,重写run()方法,调用start()开启线程
需要注意:线程开启不是立即执行,而是要看CPU的调度
还需要注意,调用run()方法和调用start()方法的区别

package com.ztece.ThreadDemo;
//创建线程方式一
//继承Thread类,重写run()方法,调用start()开启线程
import java.net.SocketOption;
/**
* 总结!!!
* 注意,线程开启不一定立即执行,由CPU调度执行
* 要注意调用run()和调用start()的区别
*/
public class ThreadDemo01 extends Thread {//继承Thread类
@Override
public void run(){//重写run()方法
//run方法线程体
for (int i = 0; i < 500; i++) {
System.out.println("=======执行run线程");
}
}
public static void main(String[] args) {//main线程,主线程
//创建一个线程对象
ThreadDemo01 threadDemo01 = new ThreadDemo01();
//调用start()方法开启线程
//threadDemo01.run();
//这里如果调用run()一定是先把run()线程体执行完,再执行main()线程体
threadDemo01.start();
//main方法线程体
for (int i = 0; i < 1000; i++) {
System.out.println("执行main线程=========");
}
}
}
1.2 通过实现Runnable接口创建线程
通过继承Thread类来创建线程的一般流程:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类作为参数,调用start()方法
实现Runnable接口的方式
- 1.创建一个实现了Runnable接口的类
- 2.实现类去实现Runnable中的抽象方法:run()
- 3.创建实现类的对象
- 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 5.通过Thread类的对象调用start()
package com.ztece.ThreadDemo;
/**
* 建议使用实现Runnable接口的方法
*/
//创建线程方式2
//
public class ThreadDemo02 implements Runnable{//实现runnable接口
@Override
public void run(){//重写run()方法
//run()线程体
for (int i = 0; i < 200; i++) {
System.out.println("========我在看代码"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
ThreadDemo02 threadDemo02 = new ThreadDemo02();
//创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(threadDemo02);//把runnable接口实现类丢入执行线程中
thread.start();//调用start()方法
for (int i = 0; i < 300; i++) {
System.out.println("我在学习多线程======="+i);
}
}
}
在开发过程中优先选择实现Runnable接口的方式创建线程:
1.实现的方式没有类的单继承的局限性
2.实现的方式更适合来处理多个线程共享数据的情况
2.静态代理模式
静态代理模式总结
1.真实对象和代理对象都要实现同一个接口
2.代理对象要代理真实角色
静态代理的好处
1.代理对象可以做到很多真实对象做不了的事情
2.真实对象专注做自己的事情
package com.ztece.ThreadDemo;
public class staticProxy {
public static void main(String[] args) {
You you = new You();//你要结婚
//you.HappyMarry();
//这里不通过这个调用,而是通过代理调用
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{
@Override
public void HappyMarry(){
System.out.println("You类被执行到了!!!");
}
}
//代理角色,助你结婚
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("结婚之后,收取礼金");
}
}
3.Lamda表达式
总结 1.lamda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹 2.前提是接口为函数式接口 3.多个参数也可以去掉参数类型,要去就都去掉,必须加上括号
什么是函数式接口?
任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口。
Lamda表达式的优点:
1.避免匿名内部类定义过多
2.可以让你的代码看起来很简洁
3.去掉了一堆没用意义的代码,只留下核心的逻辑。
package com.ztece.ThreadDemo;
import java.security.PublicKey;
public class LamdaDemo {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lamda(){
System.out.println("Lamda Lamda");
}
}
public static void main(String[] args) {
ILike like = new Like();//注意这里的like是ILike类型的
like.lamda();
like = new Like2();
like.lamda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lamda(){
System.out.println("Lamda Lamda Lamda");
}
}
like = new Like3();
like.lamda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike(){
@Override
public void lamda(){
System.out.println("Lamda Lamda Lamda Lamda");
}
};
like.lamda();
//6.用lamda简化
like = ()->{
System.out.println("Lamda Lamda Lamda Lamda Lamda");
};
like.lamda();
//7.简化lamda(去掉参数类型,去掉小括号,去掉大括号)
like = ()->System.out.println("Lamda Lamda Lamda Lamda Lamda Lamda");
like.lamda();
}
}
//1.定义一个函数式接口
interface ILike{
void lamda();
}
//2.实现类
class Like implements ILike{
@Override
public void lamda(){
System.out.println("Lamda");
}
}
4.对于线程的操作
4.1 停止
测试Stop 1.建议线程正常停止,利用次数,不建议使用死循环 2.建议使用标志位flag 3.不建议使用stop()或者destroy()等已经被淘汰的方法
package com.ztece.ThreadDemo;
public class TestStop implements Runnable{
//设置标志位
private boolean flag = true;
@Override
public void run(){//重写run()
int i = 0;
while(flag){
System.out.println("run()正在执行"+i++);
}
}
//设置一个公开的方法停止线程,调整标志位
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 < 1000; i++) {
if(i == 990){
testStop.Stop();//调用Stop(),切换标志位,使线程结束
System.out.println("线程该结束了");
}
System.out.println("main()正在执行"+i);
}
}
}
4.2 礼让
Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。
yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()
达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
package com.ztece.ThreadDemo;
public class TestYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程执行结束");
}
public static void main(String[] args) {
TestYield test = new TestYield();
new Thread(test,"a").start();
new Thread(test,"b").start();
}
}
4.3 睡眠
可以利用sleep(),放大程序中的问题。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
package com.ztece.ThreadDemo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
//模拟倒计时
public static void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);//每次睡眠一秒
System.out.println(num--);
if(num <= 0){
break;
}
}
}
//打印当前系统时间
public static void Time(){
//获取系统当前时间
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();
}
}
}
public static void main(String[] args) {
Time();
}
}
4.4 状态

package com.ztece.ThreadDemo;
public class TestState{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程结束了");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//NEW
//观察线程启动后的状态
thread.start();//启动线程
state = thread.getState();
System.out.println(state);
//只要线程不终止,就一直输出状态
while(state != Thread.State.TERMINATED){
Thread.sleep(200);
state = thread.getState();//一直在更新状态
System.out.println(state);
}
}
}
4.5 优先级
优先级第意味着获得调度的概率比较低,并不是优先级第就不运行了,这个要看CPU的调度
先设置优先级,再启动
package com.ztece.ThreadDemo;
public class TestPrioriy implements Runnable{
public static void main(String[] args) {
//主线程默认的优先级
System.out.println(Thread.currentThread().getName()+"的优先级是"+Thread.currentThread().getPriority());
TestPrioriy testPrioriy = new TestPrioriy();
Thread t1 = new Thread(testPrioriy);
Thread t2 = new Thread(testPrioriy);
Thread t3 = new Thread(testPrioriy);
Thread t4 = new Thread(testPrioriy);
Thread t5 = new Thread(testPrioriy);
t1.start();//默认的优先级
//先设置优先级,再启动
t2.setPriority(2);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
t4.setPriority(Thread.MIN_PRIORITY);
t4.start();
}
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"的优先级是"+Thread.currentThread().getPriority());
}
}
4.6 插队
package com.ztece.ThreadDemo;
//测试Join方法,可以想象为插队
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("vvvvvvvvvip线程来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);//注意格式
thread.start();
for (int i = 0; i < 400; i++) {
if(i == 200){
thread.join();//插队来了
}
System.out.println("普通线程在执行"+i);
}
}
}
4.7 用户线程和内核线程
package com.ztece.ThreadDemo;
public class TestDeamon {
public static void main(String[] args) {
YouLife youLife = new YouLife();
God god = new God();
Thread thread = new Thread(god);
//默认是false ,代表是用户线程,一般的线程都是用户线程
thread.setDaemon(true);
thread.start();
new Thread(youLife).start();
}
}
class YouLife implements Runnable{//注意,都要写Runnable接口的实现类
@Override
public void run(){
for (int i = 0; i < 100000; i++) {
System.out.println("开开心心每一天");
}
System.out.println("Goodbye World!!!");
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("God help you");
}
}
}
5.线程同步机制
关于线程的同步,一般有以下解决方法:
1. 在需要同步的方法的方法签名中加入synchronized关键字。
2. 使用synchronized块对需要进行同步的代码段进行同步。
3. 使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象。
package com.ztece.ThreadDemo.syn;
//不安全的取钱
//两个人一起去银行取同一个账户上的钱
/**
* 同步代码块
* 要锁住变化的东西 这个例子里边是 account
*/
public class unsafeBank {
public static void main(String[] args) {
Account account = new Account(500,"基金");
//两个取钱线程
Drawing boy = new Drawing(account,100,"你");
Drawing girl = new Drawing(account,50,"她");
boy.start();
girl.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(){
//锁的对象是变化的量,需要增删改的对象
synchronized (account){//获得account的锁,才能对account进行操作
//这里变化的是account需要对account加锁
//判断还有没有钱
if(account.money-drawingmoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingmoney;
nowmoney = nowmoney + drawingmoney;
System.out.println(Thread.currentThread().getName()+"手上有"+nowmoney);
System.out.println(account.name+"账户上的钱还有"+account.money);
}
}
}
package com.ztece.ThreadDemo.syn;
import java.util.concurrent.locks.ReentrantLock;
/**
* 同步方法
* 在方法的声明中加上 synchronized
*/
public class unsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"我").start();
new Thread(buyTicket,"你").start();
new Thread(buyTicket,"黄牛").start();
}
}
class BuyTicket implements Runnable{
private int ticket = 10;//票数
boolean flag = true;//标志位
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run(){
//一直买
while(flag){
try{
lock.lock();//显式的锁
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();
}
}
}
//synchronized 同步方法,锁的是this
//同步方法保证线程的安全
private void buy() throws InterruptedException {
//判断是否还有票
if(ticket<=0){
flag = false;
return ;
}
//模拟延时,放大问题
Thread.sleep(500);
//买票
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket--+"张票");
}
}
6.生产者消费者问题
6.1 管程法解决线程间通信的问题
管程法在生产者消费者模式中,其实就是建立一个缓冲区,让生产者将生产产品(数据)放入缓冲区,而消费者从缓冲区获取产品。生产者和消费者之间不直接进行通信。
package com.ztece.ThreadDemo.syn;
/**
* 管程法解决线程间通信的问题
*/
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run(){
for (int i = 0; i < 100; i++) {
try {
container.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产了第"+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++) {
try {
System.out.println("消费了第"+container.pop().id+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//商品
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) throws InterruptedException {
//如果缓冲区满了,就需要通知消费者取走产品
if(count == chickens.length){
//通知消费者取走商品
this.wait();
}
//如果缓冲区没有满,继续放入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者取走产品
public synchronized Chicken pop() throws InterruptedException {
//看看有没有产品可以取走
if(count == 0){
//等待生产者生产,消费者等待
this.wait();
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
6.2 信号灯法解决线程间通信的问题
在生产者消费者模式中,信号灯,顾名思义,是用来在生产者与消费者之间传递信号的一个旗帜。
如在一个生产与消费模式中,当生产者或消费者线程完成自己的工作,等待另一个线程进行时,便会将信号值修改用以告诉另一者:我的事情做完了,该你了。
而另一者获取信号的变化后便会做出对应的行为。在这个过程中,信号值一直被反复更改,直到所有线程均执行完毕。
package com.ztece.ThreadDemo.syn;
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 < 20; i++) {
if(i%2 == 0){
try {
this.tv.play("快乐大本营正在播放");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
this.tv.play("抖音");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消费者(观众)
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{
//演员表演,观众等待 T
//观众观看,观众等待 F、
String voice;//表演的节目
boolean flag = true;
//表演
public synchronized void play(String voice) throws InterruptedException {
if(!flag){
this.wait();
}
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;
}
}
6.3 线程池解决线程间通信的问题
为了避免重复的创建线程,线程池的出现可以让线程进行复用。通俗点讲,当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
package com.ztece.ThreadDemo.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
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());
service.execute(new MyThread());
//3.关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}


被折叠的 条评论
为什么被折叠?



