线程创建的方式
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
实现Runnable
1、定义MyRunnable类实现Runnable接口
2、实现run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
public class TestThread3 implements Runnable {
//重写run方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启我们的线程,代理
new Thread(testThread3).start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习"+i);
}
}
}
继承Thread类
启动线程:子类对象.start()
TestThread3 testThread3 = new TestThread3();
testThread3.start();
实现Runnable接口
启动线程:传入目标对象+Thread对象.start()
TestThread3 testThread3 = new TestThread3();
new Thread(testThread3).start();
并发问题
//获得当前线程
Thread.currentThread();
//获得当前执行线程的名字
Thread.currentThread().getName()
//模拟延时
Thread.sleep(x);//x:毫秒值
Callable
1. 实现Callable接口,需要返回值类型
2. 重写call()方法,需要抛出异常
3. 创建目标对象
4. 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(x);//x:线程数
5. 提交执行:Future<Boolean> result1 =ser.submit(t1);//t1:对象名
6. 获取结果:boolean r1 = result1.get();
7. 关闭服务:ser.shutdownNow();
Lamda表达式
函数式接口
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
Lambda表达式
( ) - > { };
- Lambda表达式只能有一行代码的情况下此才能简化成一行,如果有多行,那么就用代码块包裹。
- 前提是接口为函数式接口
- 多个参数也可以去掉参数类型,要去掉需要全部去掉,必须加上括号
线程状态
线程方法
setPriority(int newPriority)//更改线程的优先级
static void sleep(long millis)//休眠
void join()//等待该线程终止,插队
static void yield()//暂停当前正在执行的线程对象,并执行其他线程,礼让不一定成功,让cpu重新调度,看cpu
void interrupt()//中断线程
boolean isAlive()//测试线程是否处于活动状态
停止线程
- 不推荐使用JDK提供的stop()/destroy()方法【已废弃】
- 推荐线程自己停止下来
- 建议使用一个标志位进行终止变量当flag=false,则终止线程运行
线程休眠
sleep存在异常InterruptedException
模拟网络延时:放大问题的发生性
Date date = new Date(System.currentTimeMillis());//获取系统当前时间
new SimpleDateFormat("HH:mm:ss").format(date);//时间格式化
每个对象都有一个锁,sleep不会释放锁;
线程状态
Thread.State state = new Thread().getState();//观察状态
Thread.State.TERMINATED;//线程终止状态
线程优先级
getPriority();//获取优先级
setPriority(int x);//改变优先级x:1——10之间
setPriority(Thread.MAX_PRIORITY);//设置最大优先级10
守护(daemon)线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
setDaemon(true);//设置守护线程
并发
线程同步其实就是一种等待机制
对象的等待池形成队列
保证线程安全性需要队列+锁
锁(synchronized)
弊端
一个线程持有锁会导致其他所有需要次锁的线程挂起
会影响程序性能
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
同步方法
public synchronized void method(int args){}
//缺陷:若将一个大的方法申明为synchronized将会影响效率
同步块
synchronized(obj){}//obj是需要增删改的对象
//JUC安全类型集合
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();//安全集合,无需添加同步锁
死锁
多个线程互相抱着对方需要的资源,然后形成僵持
Lock(锁)
可重入锁 ReentrantLock类实现了Lock
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
lock.lock();
try{
//保证线程安全的代码
}finally{
lock.unlock();
//如果同步代码有异常,要将unlock()写入finally语句块
}
}
}
synchronized 与Lock的对比
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的拓展性(提供更多的子类)
- 有限使用顺序
Lock > 同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
线程协作
线程通信
wait()//表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout)//指定等待的毫秒数
notify()//唤醒一个处于等待状态的线程
notifyAll()//唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
//注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException
生产者消费者模型
利用缓冲区解决:管程法
package com.kuang.demo4;
//生产者、消费者、产品、缓冲区(生产、消费)
public class TestPC {
public static void main(String[] args) {
SynContiner continer = new SynContiner();
new Product(continer).start();
new Customer(continer).start();
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//生产者
class Product extends Thread{
SynContiner continer;
public Product(SynContiner continer) {
this.continer = continer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
continer.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Customer extends Thread{
SynContiner continer;
public Customer(SynContiner continer) {
this.continer = continer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+continer.pop().id+"只鸡");
}
}
}
//缓冲区
class SynContiner implements Runnable{
//定义产品数量
int count = 0;
//定义容器大小
Chicken[] chickens = new Chicken[10];
@Override
public void run() {
}
//生产方法
public synchronized void push(Chicken chicken){
if (count==10){
//如果超过十个进入等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//低于10个可以继续生产
chickens[count]=chicken;
count++;
notifyAll();
}
//消费方法
public synchronized Chicken pop(){
if (count==0){
//如果没有产品,进入等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
notifyAll();
return chicken;
}
}
信号灯法,标志位解决
package com.kuang.demo4;
public class TestMark {
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){
tv.make("收看快乐大本营");
}else {
tv.make("收看广告");
}
}
}
}
//观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.see();
}
}
}
//节目
class TV{
String voice;//表演的节目
boolean flag = true;//标志位
//表演方法
public synchronized void make(String voice){
if (!flag){//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演节目"+voice);
this.notifyAll();
this.flag = !this.flag;
this.voice = voice;
}
//观看方法
public synchronized void see(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
this.flag = !this.flag;
System.out.println("观看节目");
}
}
线程池
//1、创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);//参数为线程池大小
//2、执行
service.execute(new MyThread());//MyThread():Runnable接口实现类
//3、关闭链接
service.shutdown();
package com.kuang.demo4;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
线程总结
package com.kuang.demo4;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestNew {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
MyThread2 myThread2 = new MyThread2();
new Thread(myThread2).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//继承Callable接口
class MyThread3 implements Callable{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
return 100;
}
}