多线程
-
重点:线程实现、线程同步
-
程序是指令和数据的有序集合,是一个静态的概念,进程是执行程序的一次执行过程,是一个动态的概念,通常一个进程中可以包含若干个线程,一个进程中至少有一个线程,线程是CPU调度和执行的单位。
-
线程是模拟出来的,真正的多线程是指多个cpu,即多核,如服务器,切换的很快。
核心概念:
- 线程就是独立的执行路径
- 在程序运行时即使自己没有创建线程后台也会有多个线程,如主线程,gc线程(JVM给的,守护线程)
- main()为主线程,为系统的入口,用于执行整个程序。
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器(cpu)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
线程创建
Thread(重点)
- 自定义线程继承Thread类
- 重写run()方法,编写线程执行体,比如说下载方法
- 创建线程对象,调用start()方法启动线程
package multithread.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url;//网站名字
private String name;
public TestThread2(String url,String name){//构造器传参
this.url = url;
this.name = name;
}
//下载图片的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名为:"+name);
}
public static void main(String[] args) {
//主线程
TestThread2 th1 = new TestThread2("https://tse3-mm.cn.bing.net/th/id/OIP-C.fpIhqxZE7KYzR3SWiFDKVQHaI4?w=186&h=223&c=7&r=0&o=5&dpr=1.25&pid=1.7","jinshijia1.jpg");
TestThread2 th2 = new TestThread2("https://tse4-mm.cn.bing.net/th/id/OIP-C.1TGiIUUhM-MPPDieIQPvPwHaKC?pid=ImgDet&rs=1","jinshijia2.jpg");
TestThread2 th3 = new TestThread2("https://image.ijq.tv/201607/15/16-54-32-72-10.jpg","jinshijia3.jpg");
//同时执行
th1.start();
th2.start();
th3.start();
}
}
//下载器
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方法出现问题");
}
}
}
Runnable(重点)
- 实现了Runnable接口
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程,new Thread(th1).start();
- 要把目标对象丢到Thread对象里面,推荐使用,避免单继承局限性,方便同一个对象被多个线程使用。
package multithread.thread;
//实现runnable接口,重写run方法
public class TestThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("ddd");
}
}
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("mmmm");
}
}
}
package multithread.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Runnable,实现多线程同步下载图片
public class TestThread2 implements Runnable{
private String url;//网站名字
private String name;
public TestThread2(String url,String name){//构造器传参
this.url = url;
this.name = name;
}
//下载图片的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名为:"+name);
}
public static void main(String[] args) {
//主线程
TestThread2 th1 = new TestThread2("https://tse3-mm.cn.bing.net/th/id/OIP-C.fpIhqxZE7KYzR3SWiFDKVQHaI4?w=186&h=223&c=7&r=0&o=5&dpr=1.25&pid=1.7","jinshijia1.jpg");
TestThread2 th2 = new TestThread2("https://tse4-mm.cn.bing.net/th/id/OIP-C.1TGiIUUhM-MPPDieIQPvPwHaKC?pid=ImgDet&rs=1","jinshijia2.jpg");
TestThread2 th3 = new TestThread2("https://image.ijq.tv/201607/15/16-54-32-72-10.jpg","jinshijia3.jpg");
//同时执行
new Thread(th1).start();
new Thread(th2).start();
new Thread(th3).start();
}
}
//下载器
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方法出现问题");
}
}
}
- 多线程情况下操作统一资源,线程不安全
package multithread.thread;
/*
多个线程同时操作同一个线程
买火车票的例子
问题:
多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
*/
public class TestThread4 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-拿到了第:"+ticketNums--+"票");
}
}
public static void main(String[] args) {
new Thread(new TestThread4(),"小明").start();
new Thread(new TestThread4(),"老师").start();
new Thread(new TestThread4(),"小红").start();
}
}
- 龟兔赛跑案例
package multithread.thread;
public class Race implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"--跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if (winner != null){
return true;
}if (steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is" + winner);
return true;
}
return false;
}
public static void main(String[] args) {
new Thread(new Race(),"兔子").start();
new Thread(new Race(),"乌龟").start();
}
}
Callable(了解)
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行服务
-
ExecutorService ser = Executors.newFixedThreadPool(3);//线程池
-
-
提交执行
-
Future<Boolean> result1 = ser.submit(th1);//提交线程 Future<Boolean> result2 = ser.submit(th2); Future<Boolean> result3 = ser.submit(th3);
-
-
获取结果
-
boolean r1 = result1.get(); boolean r2 = result2.get(); boolean r3 = result3.get();
-
-
关闭服务
-
ser.shutdownNow();
-
package multithread.callable;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//创建方式三:实现callable接口
/*
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 th1 = new TestCallable("https://tse3-mm.cn.bing.net/th/id/OIP-C.fpIhqxZE7KYzR3SWiFDKVQHaI4?w=186&h=223&c=7&r=0&o=5&dpr=1.25&pid=1.7","jinshijia1.jpg");
TestCallable th2 = new TestCallable("https://tse4-mm.cn.bing.net/th/id/OIP-C.1TGiIUUhM-MPPDieIQPvPwHaKC?pid=ImgDet&rs=1","jinshijia2.jpg");
TestCallable th3 = new TestCallable("https://image.ijq.tv/201607/15/16-54-32-72-10.jpg","jinshijia3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);//线程池
//提交执行
Future<Boolean> result1 = ser.submit(th1);//提交线程
Future<Boolean> result2 = ser.submit(th2);
Future<Boolean> result3 = ser.submit(th3);
//获取结果
boolean r1 = result1.get();
boolean r2 = result2.get();
boolean r3 = result3.get();
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
//关闭服务
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();
}
}
}
Lambda表达式jdk8新增
- Functional Interface:函数式接口
- 任何接口如果只包含唯一一个抽象方法,那么它就是一个函数式接口
- 对于函数式接口,可以通过lambda表达式来创建该接口的对象
- 避免匿名内部类定义过多
- 不用类的名称,不用接口,直接方法后面的部分
//6.用lambda简化
like = () -> {
System.out.println("lambda5");
};
like.lambda();
package multithread.lambda;
public class TestLambda1 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("lambda2");
}
}
public static void main(String[] args) {
new Like().lambda();
new Like2().lambda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("lambda3");
}
}
new Like3().lambda();
//5.匿名内部类:没有类的名称,必须借助接口或者父类
ILike like = new ILike() {
@Override
public void lambda() {
System.out.println("lambda4");
}
};
like.lambda();
//6.用lambda简化
like = () -> {
System.out.println("lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike{
void lambda();
}
//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("lambda1");
}
}
- 简化lambda
- 总结:
- lambda表达式只能有一行代码的情况下才能简化花括号,多行代码用花括号
- 前提是接口为函数式接口
- 多个参数也可以去掉参数,要去掉都去掉,必须给参数加上括号
package multithread.lambda;
public class TestLambda2 {
public static void main(String[] args) {
ILove love = null;
// //lambda表达式
// 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(520);
}
}
interface ILove{
void love(int a);
}
-
静态代理模式:
-
//真实对象:runnable接口 代理对象:Thread new Thread( () -> System.out.println("爱你")).start();
-
静态代理模式总结:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
线程状态
线程方法
- setPriority(int newPriority):更改线程的优先级
- static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠
- void join():等待该线程终止
- static void yield():暂停当前正在执行的线程对象,并执行其他线程
- void interrupt():中断线程,别用这个方式
- boolean isAlive():测试线程是否处于活动状态
- Thread.currentThread().getName(),写的最多的方法
线程停止
package multithread.state;
/*
测试stop:
1.建议线程正常停止--利用次数,不建议死循环
2.建议使用标志位--设置一个标志位
3。不用使用stop或者destroy等过时或jdk不建议使用的方法
*/
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 < 1000; i++) {
System.out.println("main"+i);
if (i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程停止");//在900时停止该线程
}
}
}
}
线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进行就绪状态
- sleep可以模拟网络延时、倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
package multithread.state;
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class TestSleep{
public static void main(String[] args) {
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());
try {
while (true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if (num ==0){
break;
}
}
}
}
线程礼让
- 让当前正在执行的线程暂停,但不阻塞
- 让cpu重新调度,礼让不一定成功,看cpu心情
package multithread.state;
//线程礼让
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();//a线程
new Thread(myYield,"b").start();//b线程
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"线程终止");
}
}
Join合并线程
- 此线程完成后,再执行
package multithread.state;
//测试join方法,插队
//少用,会造成阻塞
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程:"+i);
}
}
public static void main(String[] args) {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 500; i++) {
if (i==200){
try {
thread.join();//
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main:"+i);//主线程
}
}
}
观察线程的状态
package multithread.state;
//线程中断或结束,死亡后的线程不能重启
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("aaaaabb");
}
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后
thread.start();
System.out.println(thread.getState());//runnable
while (state != Thread.State.TERMINATED){//只要线程不终止就一直输出状态
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级
- Thread.MIN_PRIORITY = 1;
- Thread.MIN_PRIORITY = 10;
- 线程优先级用数组表示,范围从1-10
- 使用setPriority(int xx):改变优先级
- 使用getPriority():获得优先级
package multithread.state;
//测试优先级
public class TestPriority {
public static void main(String[] args) {
//主线程优先级:先运行main
MyPriority myPriority = new MyPriority();
Thread thread1 = new Thread(myPriority);
Thread thread2 = new Thread(myPriority);
Thread thread3 = new Thread(myPriority);
Thread thread4 = new Thread(myPriority);
//先设置优先级,再启动
thread1.start();
thread2.setPriority(1);
thread2.start();
thread3.setPriority(4);
thread3.start();
thread4.setPriority(Thread.MAX_PRIORITY);//第3个线程
thread4.start();
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
守护线程(daemon)
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
package multithread.state;
//测试守护线程
public class TestDaemon {
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();//上帝守护线程
Thread thread1 = new Thread(you);
thread1.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("一生开心");
}
System.out.println("=====goodbye,world");
}
}
线程同步(重点)
-
锁机制:synchronized
-
多个线程操作同一个资源
-
并发:同一个对象被多个线程同时操作
-
队列和锁
-
当一个线程获得对的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
-
解决线程安全问题,但是性能低。
-
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
三大不安全案例
package multithread.synchro;
//线程不安全:三个共享一个资源,有重复的
//有负数的情况:三个都买了同一张票,所以需要排队
//加了synchronized变成同步了
public class UnsafedBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread thread1 = new Thread(buyTicket,"张三");
Thread thread2 = new Thread(buyTicket,"李四");
Thread thread3 = new Thread(buyTicket,"王五");
thread1.start();
thread2.start();
thread3.start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
//买票
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized变成同步方法,锁的是this
private synchronized void buy() throws InterruptedException {
//判断是否邮票
if (ticketNums<=0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
package multithread.synchro;
//不安全的取钱
//两个人去取钱
//synchronized默认锁的是this,需要同步块synchronized(Obj){}
public class UnsafeBank {
public static void main(String[] args) {
//账户
Accout accout = new Accout(100, "结婚基金");
Drawing you = new Drawing(accout, 50, "你");
Drawing wife = new Drawing(accout, 100, "wife");
you.start();
wife.start();
}
}
//账户
class Accout{
int money;
String name;
public Accout(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Accout accout;//账户
//取了多少钱
int drawingMoney;
//现在手里有多少钱
int nowMoney;
public Drawing(Accout accout,int drawingMoney,String name){
super(name);
this.accout = accout;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
synchronized (accout){//锁增删改查的那个对象
//判断有没有钱
if (accout.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+":钱不够");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额 = 余额 - 你取的钱
accout.money = accout.money - drawingMoney;
//手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(accout.name + "余额为:"+ accout.money);
// Thread.currentThread().getName() = this.name(),因为继承了Thread类
System.out.println(this.getName()+"手里的钱:" + nowMoney);
}
}
}
package multithread.synchro;
import java.util.ArrayList;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(2000);
System.out.println(list.size());
}
}
同步方法
-
synchronized关键字:synchronized方法和synchronized块
-
public synchronized void method(int args){}
-
synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
-
每个对象都对应一把锁
-
缺陷:若将一个大的方法声明为synchronized将会影响效率,方法里需要修改的内容才需要锁,锁的太多,浪费资源
同步块:synchronized(Obj){},锁增删改查的那个对象,变化的量
-
Obj称为同步监视器
- Obj可以是任何对象,推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步的监视器就是this,就是这个对象本身,或者是class
-
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
-
补充JUC高并发
package multithread.synchro;
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
-
多个线程各自占有一些共享资源,并且互相等待其他线程占用的资源释放才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有"两个以上对象的锁"时,就会发生"死锁"的问题
-
死锁:多个线程互相抱着对方需要的资源,然后形成僵持
解决:不能抱对方的锁,把对方的锁移出锁块 -
死锁产生的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而被阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
Lock锁
-
并发编程领域,JUC包下,通过显式定义同步锁对象来实现同步
-
ReentrantLock类实现了Lock,可重入锁,在线程安全的控制中,可以显式加锁、释放锁。
-
要手动加锁,手动解锁。
package multithread.lock;
import java.util.concurrent.locks.ReentrantLock;
//Lock锁
public class TestLock {
public static void main(String[] args) {
TestLock2 lock2 = new TestLock2();
//三个线程同时操作一个对象,不安全
new Thread(lock2).start();
new Thread(lock2).start();
new Thread(lock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
- synchronized 与 lock 的对比
- Lock是显示锁,手动开启和关闭锁,synchronized是隐式锁
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM花费较少时间来调度线程,性能好,具有更好的扩展性
- 优先顺序:
- Lock > 同步代码块 > 同步方法
线程协作
- 线程通信
- 解决线程之间的通信问题的方法,均是Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常IIIegalMonitorStateException
- wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
- wait(long timeout):指定等待的毫秒数
- notify():唤醒一个处于等待状态的线程
- notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的先调度
管程法
package multithread.cooperate;
//测试:生产者消费者模型 --利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).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++) {
container.push(new Produce(i));
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++) {
System.out.println("消费了-->"+container.pop().id+"只鸡");
}
}
}
//产品
class Produce{
int id;
public Produce(int id) {
this.id =id;
}
}
//缓冲区
class SynContainer{
Produce[] produces =new Produce[10];//容器大小
int count = 0;//定义容器计数器
//生产者放入产品
public synchronized void push(Produce produce){
//如果容器满了就需要等待消费者消费
if (count == produces.length){
//通知消费者消费
try {
this.wait();//让它等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满。就需要丢入产品
produces[count] = produce;
count++;
//可以通知消费者消费
this.notifyAll();//唤醒这个对象的所有线程
}
//消费者消费产品
public synchronized Produce pop(){
//判断能否消费
if (count == 0){
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费,就消费
count--;
Produce produce = produces[count];
//吃完了,通知生产者生产
this.notifyAll();
return produce;
}
}
信号灯法
package multithread.cooperate;
//测试生产者消费者问题2:信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
Player player = new Player(tv);
Watcher watcher = new Watcher(tv);
player.start();
watcher.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.play("快乐大本营播放中");
}else {
tv.play("广告播放");
}
}
}
}
//消费者-观众
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.watch();
}
}
}
//产品--节目
class TV{
//演员表演,观众等待
//观众观看,演员等待
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;
}
}
线程池
- 提前创建好多个线程放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁的创建销毁、实现重复利用。类是公共交通工具。提高性能
- ExecutorService:真正的线程池接口,常用子类ThreadPoolExecutor
- void execute(Runnable command):执行任务、命令,没有返回值,一般用来执行Runnable
- Futuresubmit(Calable task):执行任务,有返回值,一般用来执行Callable
- void shutdown():关闭线程池
- Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
package multithread.pool;
import java.util.concurrent.Executor;
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);
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
//submit(callable的任务):用来执行callable
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
完通知演员表演
this.notifyAll();
this.flag =!this.flag;
}
}
#### 线程池
- **提前创建好多个线程放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁的创建销毁、实现重复利用。类是公共交通工具。提高性能**
- **ExecutorService:真正的线程池接口,常用子类ThreadPoolExecutor**
- **void execute(Runnable command):执行任务、命令,没有返回值,一般用来执行Runnable**
- **<T>Future<T>submit(Calable<T> task):执行任务,有返回值,一般用来执行Callable**
- **void shutdown():关闭线程池**
- **Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池**
```java
package multithread.pool;
import java.util.concurrent.Executor;
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);
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
service.execute(new MyThread());//用来执行runnable
//submit(callable的任务):用来执行callable
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}