总结
1、进程包含多个线程,至少一个主线程
2、线程三种创建方式:
继承Thread类
实现Runnanble接口
实现Callable接口 重写call方法 开启线程池,
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> r1 = ser.submit(t1);
r1.get()
3、Lambda表达式:函数式接口,只包含一个抽象方法
4、线程状态:
新建
就绪
运行
阻塞
死亡
5、线程方法:
setPriority(int newPriority) :更改线程优先级 优先级1-10 默认5 优先级高只是调度概率高,不代表一定第一个调用
static void sleep(Long mills) :在指定的毫秒数内让当前正在执行的线程休眠
void join() :插队让其他线程暂停,等待该线程终止 其他线程阻塞,先执行本线程
static void yield() :暂停当前正在执行的线程对象,并执行其他线程 线程暂停不阻塞,不一定礼让成功,看cpu调度
void interrupt() : 中断线程
boolean isAlive() :测试线程是否处于活动状态
6、线程的停止不建议使用JDK的stop方法,建议设置一个flag,去停止代码的执行
7、守护线程:
thread.setDaemon(true);//true为设置为守护线程,如果不设置为守护线程,则
thread.start();
启动守护线程,再开启用户线程
用户线程结束,守护线程也会结束
应用:后台记录操作日志、监控内存
8、线程同步:
同步解决方法:
synchronized方法
synchronized代码块
lock锁 lock.lock(); lock.unlock();
9、死锁:对方都锁着自己需要的对象资源,解决办法就是 避免嵌套synchronized
10、线程协作:
管程法:生产者、消费者、缓冲区
信号灯法:立一个标志,如果为true,就让线程 等待,如果为false,就通知另一个人,将两个人衔接起来
线程
任务、进程、线程、多线程
进程是系统分配的
进程中包含若干个线程,至少包含一个主线程(main线程)
1、线程的创建
三种创建方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口(了解)
Thread类
//创建线程方式一:集成Thread类,重写Run方法,调用start开启线程
//线程开启不一定立即执行,由cpu调度执行
public class ThreadTest extends Thread {
@Override
public void run() {
super.run();
//run方法线程体
for (int i = 0; i <20 ; i++) {
System.out.println("我在看代码"+i+"次");
}
}
public static void main(String[] args) {
//mian主线程
//创建线程对象
ThreadTest threadTest = new ThreadTest();
// threadTest.run(); //run方法是先执行run再执行下面的代码
threadTest.start(); //start方法是同时执行run方法和下面的for循环
for (int i = 0; i <200 ; i++) {
System.out.println("我在学习多线程"+i+"次");
}
}
}
实现多线程下载图片
//练习Thread,实现多线程同步下载图片
public class ThreadTest01 extends Thread{
private String url;
private String name;
public ThreadTest01(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
super.run();
WebLoader webLoader =new WebLoader();
webLoader.downLoader(url,name);
System.out.println("下载文件名:"+name);
}
public static void main(String[] args) {
ThreadTest01 t1 =new ThreadTest01("https://t9.baidu.com/it/u=583874135,70653437&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=97e11c2abcd610a41c13b066b52a3779","a.jpg");
ThreadTest01 t2 =new ThreadTest01("https://t9.baidu.com/it/u=1307125826,3433407105&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=90b5ea80ecf44290e31e3bb74580d7b5","b.jpg");
ThreadTest01 t3 =new ThreadTest01("https://t9.baidu.com/it/u=1761131378,1355750940&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=452c83d05f07418798085d23a58b6628","c.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebLoader{
//下载方法
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异常");
}
}
}
Runnable
//创建线程方法二:实现Runnable接口,重写run方法,创建线程对象,执行线程需要丢入runnable接口实现类,调用start方法
public class RunnableTest implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200 ; i++) {
System.out.println("我在学习"+i);
}
}
public static void main(String[] args) {
RunnableTest test = new RunnableTest();
Thread thread =new Thread(test);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在看视频"+i);
}
}
}
多线程操作同一个对象
//多个线程操作同一个对象
//模拟抢火车票
public class ThreadTest2 implements Runnable {
private int ticketNum=10;
@Override
public void run() {
while (true) {
try {
if (ticketNum < 1) {
break;
}
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "--->拿到了弟" + ticketNum-- + "张票");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadTest2 test2 = new ThreadTest2();
new Thread(test2, "小明").start();
new Thread(test2, "黄牛").start();
new Thread(test2, "老师").start();
}
}
模拟龟兔赛跑
//模拟龟兔赛跑
public class Run 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 b = gaveOver(i, Thread.currentThread().getName());
if(b){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
public boolean gaveOver(int i,String name){
if(winner!=null){
return true;
}else {
if(i==100){
winner=name;
System.out.println("winner is"+ name);
return true;
}
return false;
}
}
public static void main(String[] args) {
Run run =new Run();
new Thread(run,"兔子").start();
new Thread(run,"乌龟").start();
}
}
Callable
//Callable
public class CallableTest implements Callable<Boolean> {
private String url;
private String name;
public CallableTest(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebLoader webLoader =new WebLoader();
webLoader.downLoader(url,name);
System.out.println("下载文件名:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest t1 =new CallableTest("https://t9.baidu.com/it/u=583874135,70653437&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=97e11c2abcd610a41c13b066b52a3779","a.jpg");
CallableTest t2 =new CallableTest("https://t9.baidu.com/it/u=1307125826,3433407105&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=90b5ea80ecf44290e31e3bb74580d7b5","b.jpg");
CallableTest t3 =new CallableTest("https://t9.baidu.com/it/u=1761131378,1355750940&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=452c83d05f07418798085d23a58b6628","c.jpg");
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r3.get();
}
}
//下载器
class WebLoader{
//下载方法
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异常");
}
}
}
2、静态代理
//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:
//代理对象可以做很多真实对象做不了的事情
//真实对象专注做自己的事情
public class StaticPoxy {
public static void main(String[] args) {
//线程就是静态代理模式,Thread作为代理人同样实现了Runnable接口,代理Runnable的真实实现对象去完成任务
//new Thread(Runnable的实现类).start();
new Poxy(new You()).happyMarry(); //
}
}
interface Marry{
void happyMarry();
}
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("xxx要结婚了");
}
}
class Poxy implements Marry{
private Marry marry;
public Poxy(Marry marry) {
this.marry = marry;
}
@Override
public void happyMarry() {
before();
marry.happyMarry();
after();
}
private void after() {
System.out.println("收尾款");
}
private void before() {
System.out.println("布置现场");
}
}
3、Lambda表达式
理解函数式接口是学习Lambda表达式的关键所在
函数式接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
- 对于函数式接口,我们可以通过lambda表达式来创建该接口对象
//推到lambda表达式,接口方法无参数
public class LambdaTest {
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like chenmeng2");
}
}
public static void main(String[] args) {
//1、正常类
ILike like =new Like();
like.lambda();
//2、静态内部类
like =new Like2();
like.lambda();
//3、局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like chenmeng3");
}
}
like= new Like3();
like.lambda();
//4、匿名内部类
like =new ILike(){
@Override
public void lambda() {
System.out.println("i like chenmeng4");
}
};
like.lambda();
//5、lambda表达式
like = ()->{System.out.println("i like chenmeng5");};
like.lambda();
}
}
//定义一个函数式接口
interface ILike{
void lambda();
}
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like chenmeng1");
}
}
//推到lambda表达式,接口方法含参数,多个参数亦可
public class LambdaTest2 {
static class Love1 implements ILove {
@Override
public void love(int a) {
System.out.println("i love chenmeng 1");
}
}
public static void main(String[] args) {
//1、正常类
ILove love = new Love();
love.love(2);
//2、静态内部类
love = new Love1();
love.love(2);
//3、局部内部类
class Love2 implements ILove {
@Override
public void love(int a) {
System.out.println("i love chenmeng 2");
}
}
love = new Love2();
love.love(2);
//4、匿名内部类
love = new ILove() {
@Override
public void love(int a) {
System.out.println("i love chenmeng 3");
}
};
love.love(2);
//5、Lambda表达式
love = (int a) -> {
System.out.println("i love chenmeng 4");
};
//6、Lambda表达式,简化去掉返回值
love.love(2);
love = (a) -> {
System.out.println("i love chenmeng 5");
};
love.love(2);
//7、Lambda表达式,简化去掉返回值和括号
love = a -> {
System.out.println("i love chenmeng 6");
};
love.love(2);
//8、Lambda表达式,去掉大括号
love=a->System.out.println("i love chenmeng 7");
love.love(2);
//总结:
// lambda表达式只有是一行代码的情况下才能写成一行,如果有多行就用代码块包裹,不能省区大括号
//必须是函数式接口,即接口内只有一个抽象方法
}
}
interface ILove {
void love(int a);
}
class Love implements ILove {
@Override
public void love(int a) {
System.out.println("i love chenmeng ");
}
}
4、线程状态
五大状态
线程方法:
- setPriority(int newPriority) :更改线程优先级
- static void sleep(Long mills) :在指定的毫秒数内让当前正在执行的线程休眠
- void join() :插队让其他线程暂停,等待该线程终止
- static void yield() :暂停当前正在执行的线程对象,并执行其他线程
- void interrupt() : 中断线程
- boolean isAlive() :测试线程是否处于活动状态
线程停止
- 不推荐使用JDK提供的stop、destory方法;
- 推荐线程自己停止下来;
- 建议建立一个标志位进行终止变量,当flag=false时,则线程终止运行;
//测试stop
//1、建议线程正常停止,利用次数,不建议死循环
//2、建议使用标志位,设置一个标志位
//3、不要使用stop、destory等过时的方法
public class ThreadStop implements Runnable {
//设置一个标志位
private static Boolean flag = true;
@Override
public void run() {
while(flag){
System.out.println("run--->Thread");
}
}
public static void main(String[] args) {
ThreadStop threadStop =new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main:====>"+i);
if(i==900){
threadStop.stop();
System.out.println("第"+i+"次,线程停止了");
break;
}
}
}
public void stop(){
flag=false;
}
}
线程休眠
- sleep(时间) 指定当前线程阻塞的毫秒数;
- sleep存在异常InterruptedException
- sleep时间达到后,线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每个对象都有一把锁,sleep不会释放锁
//模拟倒计时
public class SleepTest {
public static void tenDown() throws InterruptedException {
int time =10;
while(true){
Thread.sleep(1000);
System.out.println(time--);
if(time<0){
break;
}
}
}
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟时钟
public class SleepTest {
public static void dateTime() throws InterruptedException {
Date startTime = new Date(System.currentTimeMillis());
while(true){
Thread.sleep(1000);
String time = new SimpleDateFormat("hh:mm:ss").format(startTime);
startTime = new Date(System.currentTimeMillis());//更新系统时间
System.out.println(time);
}
}
public static void main(String[] args) {
try {
dateTime();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功!看cpu心情
//测试礼让线程
//礼让不一定成功,看cpu心情
public class YieldTest {
public static void main(String[] args) {
MyYield myYield =new MyYield();
new Thread(myYield,"小明").start();
new Thread(myYield,"小暗").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+":线程停止执行");
}
}
线程Join
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 理解为强制执行插队
//模拟join,线程强行插队
public class JoinTest implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("我是vip,我来了"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
JoinTest joinTest =new JoinTest();
Thread thread =new Thread(joinTest);
thread.start();
//主线程
for (int i = 0; i < 200; i++) {
System.out.println("我是main线程,我来了"+i+"次");
if(i==100){
thread.join();
}
}
}
}
监测线程状态
//测试监测线程状态
public class StatusTest {
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("*************");
});
//1、NEW
Thread.State state = thread.getState();
System.out.println(state);
//2、RUNNABLE
thread.start();
state=thread.getState();
System.out.println("***"+state);
while(state!= Thread.State.TERMINATED){
//3、TIMED_WAITING
Thread.sleep(100);
state=thread.getState();
System.out.println(state);
}
//4、TERMINATED
state=thread.getState();
System.out.println(state);
thread.start();//重新启动线程会报错,线程结束之后就会死亡
}
}
5、线程优先级
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定 应该调度哪个线程来执行
- 线程的优先级用数字来表示,范围从1~10:min 1 max 10 norm 5
- 使用以下方式改变或获取优先级。getPriority();setPriority(int 5)
- 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看cpu的调度
- 默认优先级为5
//测试线程的优先级
public class PriorityTest {
public static void main(String[] args) {
System.out.println("主线程");
MyThread myThread =new MyThread();
Thread thread1 = new Thread(myThread,"a");
Thread thread2 = new Thread(myThread,"b");
Thread thread3 = new Thread(myThread,"c");
Thread thread4 = new Thread(myThread,"d");
//先设置优先级,再启动线程
thread1.setPriority(Thread.MIN_PRIORITY);
thread1.start();
thread2.setPriority(Thread.NORM_PRIORITY);
thread2.start();
thread3.start();
thread4.setPriority(Thread.MAX_PRIORITY);
thread4.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"的优先级:"+Thread.currentThread().getPriority());
}
}
6、守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如:后台记录操作日志,监控内存,垃圾回收等等
//测试守护线程
//用户线程结束之后,守护线程也会结束,不过结束回收机制需要时间
public class TestDaemon {
public static void main(String[] args) {
You you = new You();
God god = new God();
Thread thread = new Thread(god);
thread.setDaemon(true);//true为设置为守护线程,如果不设置为守护线程,则
thread.start();
new Thread(you).start();
}
}
class You implements Runnable {
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("***********开心的活着*************");
}
System.out.println("*********goodBye***********");
}
}
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("******上帝保佑你********");
}
}
}
7、线程同步
- 处理多线程问题时,多个线程同时访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步就是一种等待机制,多个需要同时访问该对象的线程进入这个对象的等待池,形成队列,等待前面的线程使用完毕,下个线程再使用;
- 队列和锁:防止多个线程访问冲突,除了队列,还需要引入锁机制(synchronized),确保线程的安全,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可;
- 存在以下问题1、锁 会导致其他线程挂起 2、加锁、释放锁会导致上下文切换,调度延时,引起性能问题 3、如果优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题;
//不安全的买票
public class ByTicket {
public static void main(String[] args) {
Ticket ticket =new Ticket();
new Thread(ticket,"a").start();
new Thread(ticket,"b").start();
new Thread(ticket,"c").start();
}
}
class Ticket implements Runnable {
private int ticket = 10;
Boolean flag = true;
@Override
public void run() {
while (flag) {
try {
Thread.sleep(1000);//延时可以放大问题所在
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
public void buy() {
if (ticket <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到了第" + ticket-- + "张票");
}
}
//不安全的取钱
public class UnSafeBank {
public static void main(String[] args) {
Account account=new Account("基金",100);
Bank you = new Bank(account,50,"you");
Bank girl = new Bank(account,100,"girl");
you.start();
girl.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;
public Bank(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
super.run();
if (account.money - drawingMoney < 0) {
System.out.println(this.getName() + "账户余额不足");
return;
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//多线程同时操作一个对象时,实际上时两个线程对同一个内存区中的对象,属性的值,进行操作
/*
当延时时间较长,两个线程在此以下代码之前相遇,则会出现下列情况
因此如果你娶了50之后,girl再取100时,此时accout.money实际上已经是50了,再取100,则
account.money为-50,所以两个线程最后显示的剩余的钱都是-50
*/
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"剩余的钱是:"+account.money);
System.out.println(this.getName()+"现在的钱是:"+nowMoney);
}
}
//线程不安全集合
public class UnSafeList {
public static void main(String[] args) {
List<String> list= new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
//9996 两个线程同时添加到list中的同一个位置,造成覆盖,
//才会造成list.size小于10000
}
}
同步解决方法
- synchronized方法
//synchronized同步方法,锁的this
public synchronized void buy() {}
- synchronized代码块
synchronized(obj){
}
需要修改内容的部分才需要锁,查询部分不需要锁,锁的太多,浪费资源
//安全的买票 只需在买票的方法上加synchronized修饰符
public class ByTicket {
public static void main(String[] args) {
Ticket ticket =new Ticket();
new Thread(ticket,"a").start();
new Thread(ticket,"b").start();
new Thread(ticket,"c").start();
}
}
class Ticket implements Runnable {
private int ticket = 10;
Boolean flag = true;
@Override
public void run() {
while (flag) {
try {
Thread.sleep(1000);//延时可以放大问题所在
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
//synchronized同步方法,锁的this
public synchronized void buy() {
if (ticket <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到了第" + ticket-- + "张票");
}
}
//安全的取钱,将操作账户对象放到代码块中
public class UnSafeBank {
public static void main(String[] args) {
Account account=new Account("基金",10000);
Bank you = new Bank(account,50,"you");
Bank girl = new Bank(account,100,"girl");
you.start();
girl.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;
public Bank(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//锁的对象就是变化的量,需要增删改的对象
synchronized (account){
if (account.money - drawingMoney < 0) {
System.out.println(this.getName() + "账户余额不足");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//多线程同时操作一个对象时,实际上时两个线程对同一个内存区中的对象,属性的值,进行操作
/*
当延时时间较长,两个线程在此以下代码之前相遇,则会出现下列情况
因此如果你娶了50之后,girl再取100时,此时accout.money实际上已经是50了,再取100,则
account.money为-50,所以两个线程最后显示的剩余的钱都是-50
*/
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"剩余的钱是:"+account.money);
System.out.println(this.getName()+"现在的钱是:"+nowMoney);
}
}
}
线程不安全集合变安全
public class UnSafeList {
public static void main(String[] args) {
List<String> list= new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
//主线程不加sleep,主线程会提前跑完,
//则list.size的值可能不是10000,因为其他线程还没有执行结束
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
//9996 两个线程同时添加到list中的同一个位置,造成覆盖,才会造成list.size小于10000
}
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC线程安全集合
public class TestJUC {
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(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
8、死锁
//死锁:多个线程抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
MakeUp a =new MakeUp(0,"a");
MakeUp b =new MakeUp(1,"b");
a.start();
b.start();
}
}
class LipStick {
}
class Mirror {
}
class MakeUp extends Thread {
static LipStick lipStick=new LipStick();
static Mirror mirror=new Mirror();
int choice;
String girlName;
public MakeUp(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeUp();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void makeUp() throws InterruptedException {
if (choice == 0) {
synchronized (lipStick) {
Thread.sleep(1000);
System.out.println(this.girlName + "获得口红的锁");
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
}
} else {
synchronized (mirror) {
Thread.sleep(2000);
System.out.println(this.girlName + "获得镜子的锁");
synchronized (lipStick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
}
//解决死锁只需要将synchronized代码块提出
public void makeUp() throws InterruptedException {
if (choice == 0) {
synchronized (lipStick) {
Thread.sleep(1000);
System.out.println(this.girlName + "获得口红的锁");
}
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
} else {
synchronized (mirror) {
Thread.sleep(2000);
System.out.println(this.girlName + "获得镜子的锁");
}
synchronized (lipStick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
9、Lock锁
- 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当
- java.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象枷锁,线程开始访问共享资源之前应先获得Lock对象
- ReentrantLock类实现了lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentransLock,可以显示加锁,释放锁。
//测试Lock锁
public class LockTest {
public static void main(String[] args) {
Test test = new Test();
new Thread(test, "a").start();
new Thread(test, "b").start();
new Thread(test, "c").start();
}
}
class Test implements Runnable {
int ticketNum = 10;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticketNum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNum-- + "张票");
} else {
break;
}
} finally {
lock.unlock();
}
}
}
}
synchronized和Lock的对比:
- Lock是显式锁,(手动开启和关闭锁,别忘记关锁);synchronized是隐式锁,出了作用域自动释放。
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,jvm将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
10、线程协作-生产者消费者模式
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
-
对于生产者,没有生产产品之前,要通知消费者等待,生产产品之后,要通知 消费者消费
-
对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新产品以供消费
-
生产者消费者问题中,仅有synchronized是不够的:1、synchronized可阻止并发更新同一个共享资源,实现了同步 2、synchronized 不能用来实现不同线程之间的消息传递
解决方式一:管程法 -
生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
-
消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
-
缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿数据
解决方式二:信号灯法