目录
一、简介
- 程序是指令和数据的有序集合,是一个静态概念
- 进程是执行程序的一次执行过程,是一个动态概念
- 通常在一个进程中可以包含若干个线程
//主线程
public static void main(String[] args) {
}
二、创建线程
- 方法一:继承Thread类,重写run()方法,调用start开启线程(注意:线程开启后不一定立即执行,由CPU调度执行)
public class ThreadDemo01 extends Thread{
//重写run()方法
public void run(){
//run方法线程体
for (int i = 0; i < 10; i++) {
System.out.println("run"+i);
}
}
//main线程,主线程
public static void main(String[] args) {
//创建一个线程对象
ThreadDemo01 threadDemo01 = new ThreadDemo01();
//调用start方法开启线程
threadDemo01.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
- 方法二:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法(优点:避免单继承的局限性,方便同一个对象被多个线程使用)
public class ThreadDemo03 implements Runnable{
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("run"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
ThreadDemo03 threatDemo03 = new ThreadDemo03();
//创建线程对象,通过线程对象来开启我们的线程 (代理)
Thread thread = new Thread(threatDemo03);
thread.start();
//new Thread(threatDemo03).start();
for (int i = 0; i <1000 ; i++) {
System.out.println("main"+i);
}
}
}
多线程同时操作同一个对象
//多个线程同时操作同一个对象
//抢票
public class ThreadDemo04 implements Runnable{
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+(ticketNums--)+"张票");
}
}
public static void main(String[] args) {
ThreadDemo04 threadDemo04 = new ThreadDemo04();
new Thread(threadDemo04,"小明").start();
new Thread(threadDemo04,"小红").start();
new Thread(threadDemo04,"黄牛").start();
}
}
- 方法三:实现Callable接口(需要返回值);重写call方法(需要抛出异常);创建目标对象;创建执行服务(ExecutorService ser = Executors.newFixedThreadPool(1););提交执行(Future< Boolean > result1 = ser.submit(t1););获取结果(boolean r1 = result1.get(););关闭服务(ser.shutdownNow();)
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.URL;
import java.util.concurrent.*;
//练习:用多线程下载图片
public class CallableDemo01 implements Callable<Boolean> {
private String url;
private String name;
public CallableDemo01(String url , String name){
this.url=url;
this.name= name;
}
//线程体
@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.down(url,name);
System.out.println("下载"+name);
return true;
}
public static void main(String[] args) {
CallableDemo01 t1 = new CallableDemo01("https://cn.bing.com/images/search?view=detailV2&ccid=8olSEek1&id=337FFE50570CBEA0E113BEC16941B5C9B32B8412&thid=OIP.8olSEek1RZjSQDgPizhxEAHaJ3&mediaurl=https%3A%2F%2Fscpic.chinaz.net%2Ffiles%2Fpic%2Fpic9%2F202009%2Fapic27858.jpg&exph=866&expw=650&q=%e5%9b%be%e7%89%87&simid=607999977435913615&form=IRPRST&ck=2ECCC8716D95A1EC8BFBBF273B74945F&selectedindex=0&ajaxhist=0&ajaxserp=0&vt=0&sim=11","e:/1.jpg");
CallableDemo01 t2 = new CallableDemo01("https://cn.bing.com/images/search?view=detailV2&ccid=nRlAFygd&id=1C8280D2D75B8653FE4F87817387F57189B5AA41&thid=OIP.nRlAFygdctTCHmIWN7GxRwHaEK&mediaurl=https%3A%2F%2Fwww.keaidian.com%2Fuploads%2Fallimg%2F190424%2F24110307_8.jpg&exph=1080&expw=1920&q=%e5%9b%be%e7%89%87&simid=608051753279948997&form=IRPRST&ck=96BDBB01D66DFC3247792098BAA2CA28&selectedindex=1&ajaxhist=0&ajaxserp=0&vt=0&sim=11","e:/2.jpg");
CallableDemo01 t3 = new CallableDemo01("https://cn.bing.com/images/search?view=detailV2&ccid=xSB%2F5yZF&id=3AD3C13EEA5DE64CBD1E2D690FEA5DFB10106011&thid=OIP-C.xSB_5yZFzr3OR8HJ6IiAzAHaEq&mediaurl=https%3A%2F%2Fts1.cn.mm.bing.net%2Fth%2Fid%2FR-C.c5207fe72645cebdce47c1c9e88880cc%3Frik%3DEWAQEPtd6g9pLQ%26riu%3Dhttp%253a%252f%252fimg95.699pic.com%252fphoto%252f50077%252f2014.jpg_wh860.jpg%26ehk%3DF6l1C0wu%252bIt8eAzaHiQOvG7EAFjtMKGbgHMLAbi8XP4%253d%26risl%3D%26pid%3DImgRaw%26r%3D0&exph=541&expw=860&q=%e5%9b%be%e7%89%87&simid=608034745203441249&form=IRPRST&ck=12C66ABF14C2933DE1D50A05D744BE59&selectedindex=2&ajaxhist=0&ajaxserp=0&vt=0&sim=11","e:/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);
//获取结果(抛出异常)
try {
boolean rst1=r1.get();
boolean rst2=r2.get();
boolean rst3=r3.get();
System.out.println(rst1);
System.out.println(rst2);
System.out.println(rst3);
}catch (Exception e){
e.printStackTrace();
}
//关闭服务
ser.shutdownNow();
}
}
//下载器
class WebDownloader{
//下载方法
public void down(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
}catch (Exception e){
e.printStackTrace();
}
}
}
三、静态代理对象
//静态代理
//真实对象和代理对象都要实现同一个接口;代理对象要代理真实的角色
//代理的好处:代理对象可以做很多真实对象做不了的事;真实对象可以专心做自己的事
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}
//定义接口
interface Marry{
public void HappyMarry();
}
//真实对象类
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("你要结婚了");
}
}
//代理对象类
class WeddingCompany implements Marry{
public Marry target;//需要被代理的目标对象
public WeddingCompany(Marry target) {
this.target = target;//真实对象
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
public void before(){
System.out.println("婚前布局");
}
public void after(){
System.out.println("婚后结尾款");
}
}
四、Lamda表达式
- 函数式接口:任何接口如果只包含唯一一个抽象方法,那么它就是一个函数式接口
例如:
public interface Runnable{
public abstract void run();
}
- 对于函数式接口,可以通过Lambda表达式来创建该接口的对象
public class Demo01 {
//3.静态内部类
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();
//静态内部类的调用方法
ILike like2 = new Like2();
like2.Lambda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void Lambda() {
System.out.println("i like lambda3");
}
}
//局部内部类的调用方法
ILike like3 = new Like3();
like3.Lambda();
//5.匿名内部类,没有类的名称,必须借助接口或父类
ILike like4 = new ILike() {
@Override
public void Lambda() {
System.out.println("i like lambda4");
}
};
//匿名内部类的调用方法
like4.Lambda();
//6.用Lambda简化
ILike like5 = ()->{
System.out.println("i like lambda5");
};
like5.Lambda();
}
}
//1.定义一个函数接口
interface ILike{
void Lambda();
}
//2.实现类
class Like implements ILike{
@Override
public void Lambda() {
System.out.println("i like lambda");
}
}
五、线程停止
- 建议线程正常停止(利用次数,不建议死循环)
- 建议使用标志位(设置一个标志位)
- 不要使用stop或destroy等方法,自己建方法
public class Demo01 implements Runnable{
//设置标识位
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) {
Demo01 demo01 = new Demo01();
Thread thread = new Thread(demo01);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if (i==900){
//调用stop方法转换标志位,让线程停下来
demo01.stop();
System.out.println("线程停止了");
}
}
}
}
六、线程休眠
- sleep(时间) 指当前线程阻塞的毫秒数
- sleep存在异常InterruptedException,需要抛出
- sleep时间达到后线程进入就绪状体
- sleep可以模拟网络延时、倒计时等
- 每个对象都有一个锁,sleep不会释放锁
//模拟网络延迟:可以放大问题的发生性
public class Demo01 implements Runnable{
private int ticket = 10;
@Override
public void run() {
while (true) {
if (ticket<0){
break;
}
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticket-- + "张票");
}
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
new Thread(demo01,"小明").start();
new Thread(demo01,"老师").start();
new Thread(demo01,"黄牛").start();
}
}
public class Demo02 {
public static void main(String[] args) {
numDown();
timeDown();
}
//倒计时
public static void numDown(){
int num = 10;
while (true){
try {
Thread.sleep(1000);
System.out.println(num--);
}catch (Exception e){
e.printStackTrace();
}
if (num<0){
break;
}
}
}
//获取时间
public static void timeDown(){
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());//获取当前系统时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
}catch (Exception e){
e.printStackTrace();
}
}
}
}
七、线程礼让
- 让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态,让cpu重新调度
- 礼让不一定成功
public class Demo01 {
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()+"线程结束");
}
}
八、线程强制执行
- Join 合并线程,待此线程执行完成后,在执行其他线程(类似于插队)
- 需要抛出异常
public class Demo01 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
Thread thread = new Thread(demo01);
thread.start();
for (int i = 0; i < 500; i++) {
if (i==200){
try {
new Thread(demo01).join();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("主线程"+i);
}
}
}
九、观察线程状态
public class Demo01 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("//");
});
//观察new状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动状态
thread.start();
state = thread.getState();
System.out.println(state);
while (state!=Thread.State.TERMINATED){//只要线程不终止,就会一直循环
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}
}
}
十、线程优先级
使用getPriority()和setPriorty()进行获取和设置
一般情况下优先级高的先执行
public class Demo01 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
t1.start();//优先级默认是5
t2.setPriority(1);
t2.start();
t3.setPriority(8);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//这里是优先级的最大值,即 MAX_PRIORITY=10
t4.start();
t5.setPriority(7);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
}
}
十一、守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕
public class Demo01 {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false表示用户线程,正常线程都是用户线程
thread.start();//守护线程启动
new Thread(you).start();//用户线程启动
}
}
//守护线程
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("!!!!!保护!!!!!");
}
}
}
//用户线程
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("Hello "+i);
}
System.out.println("===============goodbye=============");
}
}
十二、线程同步(队列 加 锁)
- 同步方法(在方法中加入synchronized变为同步方法,默认对象是该方法)
public class Demo01 {
public static void main(String[] args) {
GrabTicket grabTicket = new GrabTicket();
new Thread(grabTicket,"小明").start();
new Thread(grabTicket,"老师").start();
new Thread(grabTicket,"黄牛").start();
}
}
class GrabTicket implements Runnable{
private int ticket = 10;
boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
//synchronized 同步方法,默认锁的是this
public synchronized void buy(){
if (ticket<=0){
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
}
}
- 同步块(synchronized(Obj){…},obj为同步监听器,默认为this,锁的对象是会发生变化的对象)
public class Demo02 {
public static void main(String[] args) {
Account account = new Account("总",1000);
Bank zhang= new Bank(account,500,"zhang");
Bank wang = new Bank(account,1000,"wang");
zhang.start();
wang.start();
}
}
//账户
class Account{
String name;
int money;
public Account(String name , int money){
this.name = name;
this.money = money;
}
}
//银行取钱
class Bank extends Thread{
Account account;
int drawingMoney;//取多少钱
int nowMoney;//剩多少钱
String name;
public Bank(Account account, int drawingMoney, String name) {
this.account = account;
this.drawingMoney = drawingMoney;
this.name = name;
}
@Override
public void run() {
//锁的对象是变化的量,增删改
synchronized (account){
if (drawingMoney > account.money){
System.out.println(account.name+"钱不够");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney+drawingMoney;
System.out.println(name+"余额为:"+account.money);
System.out.println(name+"手里的钱:"+nowMoney);
}
}
}
十三、CopyOnWriteArrayList
//测试JUC安全类型的集合
public class Demo03 {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
十四、死锁
- 产生条件:
(1)互斥条件:一个资源每次只能被一个进程使用
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
(3)不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
//这样就会形成死锁
if (choice==1){
synchronized (gan){
System.out.println(this.name+"获得了枪");
sleep(100);
synchronized (car){
System.out.println(this.name+"获得了车");
}
}
}else {
synchronized (car){
System.out.println(this.name+"获得了车");
sleep(100);
synchronized (gan){
System.out.println(this.name+"获得了枪");
}
}
}
处理死锁:
public class Demo01 {
public static void main(String[] args) {
Play p1 = new Play(1,"zhang");
Play p2 = new Play(0,"lin");
p1.start();
p2.start();
}
}
//枪
class Gan{
}
//车
class Car{
}
class Play extends Thread{
static Gan gan = new Gan();
static Car car = new Car();
int choice;
String name ;
public Play(int choice,String name){
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
play();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void play() throws InterruptedException {
if (choice==1){
synchronized (gan){
System.out.println(this.name+"获得了枪");
sleep(100);
}
synchronized (car){
System.out.println(this.name+"获得了车");
}
}else {
synchronized (car){
System.out.println(this.name+"获得了车");
sleep(100);
}
synchronized (gan){
System.out.println(this.name+"获得了枪");
}
}
}
}
十五、Lock锁
public class Demo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket).start();
new Thread(ticket).start();
new Thread(ticket).start();
}
}
class Ticket implements Runnable{
int ticket = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.lock();
while (true){
if (ticket<=0){
break;
}
System.out.println(ticket--);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock();
}
}
}
Lock 与 synchronized 的对比:
- Lock是显示锁(手动开启和关闭锁),synchronized 是隐式锁(出来作用域自动关闭)
- Lock只有代码块锁 ,synchronized 有代码块锁和方法锁
- Lock锁JVM花费较少时间来调度(性能好,并且具有更好的扩展性)
十六、线程通信
wait(); //表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout); //指定等待的毫秒数
notify(); //唤醒一个处于等待转态的线程
notifyAll(); //唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
- 管程法
//测试:生产者消费者模型--->利用缓冲区解决管程法
//生产者、消费者、缓冲区、产品
public class Test {
public static void main(String[] args) {
Buffer buffer = new Buffer();
new Producer(buffer).start();
new Consumer(buffer).start();
}
}
//生产者
class Producer extends Thread{
Buffer buffer;
public Producer(Buffer buffer){
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
buffer.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
Buffer buffer;
public Consumer(Buffer buffer){
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了----->"+buffer.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id ;//编号
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class Buffer{
//需要容器的大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,需要等待消费者消费
if (count==chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//容器没满,需要投入产品
chickens[count]=chicken;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//若无产品,等待生产
if (count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
- 信号灯法
//测试生产者消费者模式2:信号灯方法,标志位解决
public class Test2 {
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("快乐大本营");
}else {
System.out.println("广告");
}
}
}
}
//消费者--->观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
//产品--->节目
class TV{
//演员表演,观众等待 --- T
//观众观看,演员等待 --- F
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(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}
十七、线程池
- 经常船创建和销毁、使用量大的资源,比如并发情况下的线程,对性能影响很大
- 提前创建好多个线程,放到线程池中,使用时直接获取,使用完放回池中
- 提高了响应速度(减少了创建新线程的时间);降低资源消耗(重复利用线程池中线程,不需要每次都创建);便于线程管理
public class Demo01 {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2. 关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}