1.创建多线程
1.1通过继承Thred并重写run方法来创建一个新的进程
public class MyThread extends Thread {
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
//这里的代码 就是一条新的执行路径
//这个执行路径的触发方式 不是调用run方法 而是通过thread对象中的start()来启动任务
for (int i = 0; i<10;i++){
System.out.println("这是 "+i);
}
}
}
2.在main函数中调用 调用start()方法来创建线程
public class dome {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for (int i = 0; i<10;i++){
System.out.println("那是 "+i);
}
}
}
多个线程并不会顺序执行,java中多线程的执行方式采用的是抢占模式:多个线程互相抢,顺序不固定
1.2创建一个接口接向Runnable,重写当中的run方法
static class MyRunnable implements Runnable{
@Override
public void run() { //获取当前正在执行的线程名字
System.out.println(Thread.currentThread().getName());
}
}
Thread.currentThread().getName()方法返回的是目前执行线程的名字
public class dome1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
//在创建线程的同时个线程取个名字
Thread t = new Thread(new MyRunnable(),"锄禾日当午");
t.start();
}
3.创建一个接口接向callable,重写当中的call方法
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
使用包装类
这里写需要执行的代码
}
return 100;这个是有返回值的线程
}
}
public static void main(String[] args) {
main方法中这样写来启动线程
Callable<Integer> c =new MyCallable() ;
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();//启动线程
task.get()//调用这个方法会让子线程全部执行完毕之后
//在执行主线程
}
2.线程休眠
Thred.sleep(毫秒为单位)使用这个方法会让程序在执行是暂停程序的执行,例如以下代码,1000毫秒 = 1,秒,意思就是每当程序走到第6行时,程序会暂停一秒钟后继续执行,这段代码体现出来的效果就是每秒打印一次数字,由于这个方法有异常,所以需要手动处理一下。
public class sleep {
public static void main(String[] args){
for (int i = 0; i < 10; i++){
System.out.println(i);
try {
Thread.sleep(1000);//以毫秒为单位 进行线程的休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.守护线程
守护用户线程,当最后一个用户线程结束时,守护线程自动结束。
main为用户线程 当main函数线程结束时,守护线程同时也会结束,
使用:线程名.setDaemon(ture)方法来将该线程设置为守护线程。
例如main中执行五次循环,子线程要执行10次,当子线程变成了守护线程,那么main循环五次结束后,子线程也跟着结束,即使当时的循环还没有结束
4.线程安全
定义一个买票方法,假设有10张票 循环卖,当票数为0时退出循环,卖完了。同时创建三个进程一起买票。理论上count=0时循环将被结束,也就是不可能会打印出负数来,可现在这段代码运行的结果就是有可能会打印出负数,这样显然是有问题的,这就是线程不安全问题,那么为了解决这个问题就得给线程加锁。
public static void main(String[] args) {
Runnable run = new Ticket();
new Thread(run).start();//创建线程1
new Thread(run).start();//创建线程2
new Thread(run).start();//创建线程3
}
static class Ticket implements Runnable {
//票数
private int count = 10;
@Override
public void run() {
while (true) {
if (count > 0) {
//卖票
System.out.println("正在准备买票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(
Thread.currentThread().getName()
+ "出票成功,余票" + count);
}else{
break;
}
}
}
}
1 同步代码块
创建一个object对象成为锁对象,利用synchronized来实现锁住,这样子多条线程进入之后就会派对执行
Object o = new Object()
synchronized(o){
这里的线程就会排队执行
if (count > 0) {
//卖票
System.out.println("正在准备买票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()
+ "出票成功,余票" + count);
}else{
break;
}
}
2.同步方法
将可能出现线程安全的代码打包成方法 并用synchronized关键字修饰这个方法,这里面的方法也会派对执行
3.显示所
//ture 表示公平锁 加了之后就会按照顺序执行线程
//不加是不公平锁 所有线程抢着执行
Lock lock = new ReentrantLock(true);
lock.lock();
这之中的代码就会排队执行
lock.unlock();
5.生产者消费者问题
假设有生产者消费者两个线程,在main函数中同时开启,我们要求的是生产者生产一次 消费者·消费一次 但是因为java的原因 有多个线程抢着执行,导致生产者还没生产 消费者就已经开始消费了,或者说消费者还没开始消费,生产者一直再生产,
为了解决这个问题 采取的方法时 当生产者在生产时,消费者睡过去 当生产者生产完了自己喊起消费者消费,同时自己睡过去,当消费者消费完时,喊起生产者生产,同时自己睡过去,以此类推,这样就不会出现两个抢占线程的问题
public class cook {
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++){
if (i%2 ==0){
f.setNameAndTaste("老干妈小米粥","香辣味");
}else {
f.setNameAndTaste("煎饼果子","甜辣味");
}
}
}
}
//服务员
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;//名字
private String taste;//味道
//true表示可以生产
private boolean flag = true;
public synchronized void setNameAndTaste(String name,String taste) {
if (flag) {
this.name = name;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();//唤醒所有的线程
try {
this.wait();//该线程睡过去
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if (!flag) {
System.out.println("服务员端走的菜名:是" + name + ",味道:" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
5.线程的六种状态
1.new:尚未启动状态
2.Runnable:正在执行状态
3.Blocked:当一个线程正在排队时的状态
4.Wating:线程被休眠处于无线等待的状态
5.TimeWating:等待被唤醒的状态
6.terminated:结束状态
6.线程池
1.缓存线程池
同时创建一定个数个线程 之后需要运行的线程都使用之前创建过的线程,当线程个数超出创建后的会自动扩容
public static void main(String[] args) {
创建一个缓存线程池
ExecutorService service = Executors.newCachedThreadPool();
//指挥向线程池中执行新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
2.定长线程池
在创建时就固定好长度,多个线程一起执行,得等待有空闲线程才能执行
/**
* 定长线程池 长度是指定的数值
* 任务加入之后的执行流程
* 1.判断线程池是否存在空线程
* 2.存在则使用
* 3.不存在空线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
* 4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空现场
*/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
3.单线程池
/**
* 单线池
* 执行流程
* 1.判断线程池的那个线程是否空闲
* 2.空闲则使用 不空闲则等待空闲后使用
*/
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"撒大声地");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"撒大声地");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"撒大声地");
}
});
}
5.周期任务 定长线程池
/**
* 周期任务 定长线程池
* 执行流程
* 1.判断线程池是否存在空线程
* * 2.存在则使用
* * 3.不存在空线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
* * 4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空现场
*/
public static void main(String[] args) {
//创建周期定长线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定时执行一次
* 参数1.定时执行的任务
* 参数2.时长数字
* 参数3.时长数字的单位 TimeUnit的常量指定
*/
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
},5, TimeUnit.SECONDS);
/**
* 周期执行任务
* 参数1.任务
* 参数2.延迟时长数字 第一次执行在什么时间以后
* 参数3.周期时长数字 每隔多久执行一次
* 参数4.时长数字的单位
*
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("大八嘎");
}
},5,1,TimeUnit.SECONDS);
}