多线程
- 进程与线程
- 并行与并发
- 多线程的创建和启动
-
- Thread类
- 创建线程的两种方式
- 方式一:继承Thread类
- 方式二 :实现Runnable接口
-
- 指定线程的名称,并获取
- 继承方式和实现方式的联系和区别
- Thread类相关的方法1
- 线程的优先级
- Thread相关方法2
-
- yield方法演示:
- join方法演示:
- sleep方法演示:
- stop方法和isAlive方法演示
- 线程的生命周期
- 线程的同步和死锁
-
- 多线程的安全问问题
-
- 解决思路
- 解决办法,同步锁
-
- 在普通方法上加上同步锁synchronized,锁的是整个对象,不是某一个方法
- 不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的对象来调用该方法,还是有共享资源的问题
- 静态方法加上synchronized,对所有对象都是同一个锁
- 给当前对象的某个代码块加上同步锁,不同的对象是不同的锁
-
- 用this锁代码块是代表当前的对象,如果在当前对象的其他方法中也有synchronized(this)的代码块,那么两者使用的都是一个同步锁
- synchronized (account)不同对象有不同的锁,只有锁一致的线程才会被阻塞挨个执行
- synchronized()的用法总结
- 死锁
- 线程通信
- 生产者和消费者
- 同步代码块,同步函数,静态同步函数的锁对象
进程与线程
进程
- 当一个程序被运行,就开启了一个进程, 比如启动了qq,word
- 程序由指令和数据组成,指令要运行,数据要加载,指令被cpu加载运行,数据被加载到内存,指令运行时可由cpu调度硬盘、网络等设备
线程
- 一个进程内可分为多个线程
- 一个线程就是一个指令流,cpu调度的最小单位,由cpu一条一条执行指令
并行与并发
并发:单核cpu运行多线程时,时间片进行很快的切换。线程轮流执行cpu
并行:多核cpu运行 多线程时,真正的在同一时刻运行
多线程的创建和启动
run() :存放多线程中运行的代码逻辑
start(): 调用该线程,运行run()方法里面存放的代码
Thread类
创建线程的两种方式
方式一:继承Thread类
public class MyThread extends Thread
{
@Override
public void run() {
System.out.println("one");
System.out.println("two");
System.out.println("three");
}
}
测试:
public class Main
{
public static void main(String[] args)
{
Thread thread=new MyThread();
thread.start();
System.out.println("1");
System.out.println("2");
System.out.println("3");
}
}
当我们调用start()方法开启线程调用后,会执行run里面的代码,此时run方法中运行的代码和主程序中strat后面运行的代码是并行执行的,没先后顺序,是异步执行
但是对于单个线程来将,他的代码执行顺序是固定的
方式二 :实现Runnable接口
Runnable 接口是一个函数式接口,因此我们可以使用lambda表达式
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
代码实现:
Thread thread1=new MyThread();
//开启线程1
thread1.start();
//开启线程2
Thread thread2=new Thread(()->{
System.out.println("run1");
System.out.println("run2");
System.out.println("run3");
});
thread2.start();
System.out.println("1");
System.out.println("2");
System.out.println("3");
此时是在一个进程中,有两个分支线程和主进程并行执行,三者互不干扰,执行顺序也不固定
指定线程的名称,并获取
public class Main
{
public static void main(String[] args)
{
Thread thread1=new MyThread();
//开启线程1
thread1.start();
//开启线程2
Thread thread2=new Thread(()->{
System.out.println("当前线程: "+Thread.currentThread().getName()+" run1");
System.out.println("当前线程: "+Thread.currentThread().getName()+" run2");
System.out.println("当前线程: "+Thread.currentThread().getName()+" run3");
},"run线程");
thread2.start();
System.out.println("1");
System.out.println("2");
System.out.println("3");
}
}
继承方式和实现方式的联系和区别
资源共享的案例:
public class MyThread implements Runnable
{
int count=1;
@Override
public void run() {
System.out.println(++count);
}
}
测试:
public class Main
{
public static void main(String[] args)
{
//开启线程2
Runnable run1=new MyThread();
Thread thread2=new Thread(run1);
thread2.start();
Thread thread3=new Thread(run1);
thread3.start();
}
}
共享了run1实现类中的count变量的值
Thread类相关的方法1
演示如果没有设置线程名称的情况下,系统给出的默认线程名称:
//开启线程2
Runnable run1=new MyThread();
Thread thread2=new Thread(run1);
thread2.start();
Thread thread3=new Thread(run1);
thread3.start();
System.out.println("run1线程的默认名称:"+thread2.getName());
System.out.println("run2线程的默认名称:"+thread3.getName());
线程的优先级
线程的优先级,就是哪个线程有较大的概率被执行
优先级使用1-10的数组表示,数字越大优先级越高,线程的默认优先级都是5
设置优先级只是增大优先执行当前线程的概率
线程可以划分优先级,优先级高的线程得到的CPU资源比较多,也就是CPU优先执行优先级高的线程对象中的任务。
设置线程优先级有助于帮助线程规划器确定下一次选中哪一个线程优先执行。
演示:
Thread t1=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程11");
System.out.println(Thread.currentThread().getName()+": 线程12");
System.out.println(Thread.currentThread().getName()+": 线程13");
},"线程1,优先级为默认级别5");
Thread t2=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程21");
System.out.println(Thread.currentThread().getName()+": 线程22");
System.out.println(Thread.currentThread().getName()+": 线程23");
},"线程2,优先级设置为7");
t2.setPriority(7);
t1.start();
t2.start();
System.out.println("-----------------1");
System.out.println("-----------------2");
System.out.println("-----------------3");
Thread相关方法2
yield方法演示:
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程11");
Thread.yield();//让当前线程执行到此时,暂停当前线程,将机会让给优先级更高的线程执行
System.out.println(Thread.currentThread().getName()+": 线程12");
System.out.println(Thread.currentThread().getName()+": 线程13");
},"线程1,优先级为默认级别5");
Thread t2=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程21");
System.out.println(Thread.currentThread().getName()+": 线程22");
System.out.println(Thread.currentThread().getName()+": 线程23");
},"线程2,优先级设置为7");
t2.setPriority(1);
t1.start();
t2.start();
System.out.println("-----------------1");
System.out.println("-----------------2");
System.out.println("-----------------3");
}
join方法演示:
Thread t1=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程11");
System.out.println(Thread.currentThread().getName()+": 线程12");
System.out.println(Thread.currentThread().getName()+": 线程13");
},"线程1,优先级为默认级别5");
Thread t2=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程21");
System.out.println(Thread.currentThread().getName()+": 线程22");
System.out.println(Thread.currentThread().getName()+": 线程23");
},"线程2,优先级设置为7");
t2.setPriority(1);
t1.start();
t2.start();
System.out.println("-----------------1");
System.out.println("-----------------2");
//阻塞当前的main方法,先执行join进行的线程的代码,执行完毕后,继续执行main方法被阻塞的代码
t2.join();//相当于把t2的run方法中的代码插入到这里执行,相当于是同步执行
System.out.println("-----------------3");
sleep方法演示:
Thread t1=new Thread(()->
{
System.out.println(Thread.currentThread().getName()+": 线程11");
System.out.println(Thread.currentThread().getName()+": 线程12");
System.out.println(Thread.currentThread().getName()+": 线程13");
},"线程1,优先级为默认级别5");
Thread t2=new Thread(()->{
try {
//当前线程睡5秒后,再执行
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+": 线程21");
System.out.println(Thread.currentThread().getName()+": 线程22");
System.out.println(Thread.currentThread().getName()+": 线程23");
},"线程2,优先级设置为7");
t2.setPriority(1);
t1.start();
t2.start();
System.out.println("-----------------1");
System.out.println("-----------------2");
System.out.println("-----------------3");
stop方法和isAlive方法演示
Thread t1=new Thread(()->
{
System.out.println(Thread.currentThread().getName()+": 线程11");
System.out.println(Thread.currentThread().getName()+": 线程12");
System.out.println(Thread.currentThread().getName()+": 线程13");
},"线程1,优先级为默认级别5");
Thread t2=new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程21");
System.out.println(Thread.currentThread().getName()+": 线程22");
System.out.println(Thread.currentThread().getName()+": 线程23");
},"线程2,优先级设置为7");
t2.setPriority(1);
t1.start();
t2.start();
System.out.println("-----------------1");
//强制停止t1线程的生命周期
t1.stop();
//判断线程2是否存活
System.out.println(t2.isAlive());
System.out.println("-----------------2");
System.out.println("-----------------3");
线程的生命周期
线程的同步和死锁
多线程的安全问问题
public class Main
{
@Test
void test()
{
//线程1
Thread t1=new Thread(()->Account.drawing(800),"微信");
//线程2
Thread t2=new Thread(()->Account.drawing(800),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
static public void drawing(Integer num)
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
解决思路
解决办法,同步锁
在普通方法上加上同步锁synchronized,锁的是整个对象,不是某一个方法
不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的对象来调用该方法,还是有共享资源的问题
同一对象调用方法:
public class Main
{
@Test
void test()
{
Account account = new Account();
//线程1
Thread t1=new Thread(()->account.drawing(800),"微信");
//线程2
Thread t2=new Thread(()->account.drawing(800),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额---所有对象共享该金额
static private Integer money=1000;
//取款
//在普通方法上加上同步锁synchronized,锁的是整个对象,不是某一个方法
//不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的对象来调用该方法,还是有共享资源的问题
public synchronized void drawing(Integer num)
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
不同对象调用方法:
public class Main
{
@Test
void test()
{
Account account1 = new Account();
Account account2 = new Account();
//线程1
Thread t1=new Thread(()->account1.drawing(800),"微信");
//线程2
Thread t2=new Thread(()->account2.drawing(800),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
//在普通方法上加上同步锁synchronized,锁的是整个对象,不是某一个方法
//不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的对象来调用该方法,还是有共享资源的问题
public synchronized void drawing(Integer num)
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
静态方法加上synchronized,对所有对象都是同一个锁
public class Main
{
@Test
void test()
{
Account account1 = new Account();
Account account2 = new Account();
//线程1
Thread t1=new Thread(()->account1.drawing(800),"微信");
//线程2
Thread t2=new Thread(()->account2.drawing(800),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
public static synchronized void drawing(Integer num)
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
给当前对象的某个代码块加上同步锁,不同的对象是不同的锁
用this锁代码块是代表当前的对象,如果在当前对象的其他方法中也有synchronized(this)的代码块,那么两者使用的都是一个同步锁
public class Main
{
@Test
void test()
{
Account account1 = new Account();
//线程1
Thread t1=new Thread(()->account1.drawing(800),"微信");
//线程2
Thread t2=new Thread(()->account1.drawing(800),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
public void drawing(Integer num)
{
synchronized (this)
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
}
synchronized (account)不同对象有不同的锁,只有锁一致的线程才会被阻塞挨个执行
public class Main
{
@Test
void test()
{
Account account1 = new Account();
Account account2 = new Account();
//现在虽然还是用一个对象调用drawing方法,但是传入的是不同的对象,即对应了不同的锁
//线程1
Thread t1=new Thread(()->account1.drawing(800,account1),"微信");
//线程2
Thread t2=new Thread(()->account1.drawing(800,account2),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
public void drawing(Integer num,Account account)
{
synchronized (account)//如果两个线程的对象相同,那么就会让线程阻塞,挨个执行,否则同步执行,互不干扰
{
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
}
synchronized()的用法总结
1.普通方法加同步锁,锁的是当前方法对应的对象,当前对象的所有加了同步锁的方法是共用一个同步锁
2.静态方法加同步锁,所有对象都共用一个同步锁
3.对代码块加同步锁,如果是synchronized (this),那么所有当前的对象的synchronized (this)的代码块都是使用同一个同步锁
4.对代码块加上同步锁,如果是synchronized (a),那么根据小括号中传入的不同的对象就有不同的锁
如果针对对象要加同步锁,那就加在方法上
如果针对某一段代码需要加上同步锁,那就直接在代码块上加同步锁
synchronized关键字不能继承
如果多个线程都拥有同一个同步锁,那么这多个线程将被阻塞,挨个执行,否则这多个线程还是并行执行,互不干扰
死锁
线程a0,需要执行方法f0
线程a1,需要执行方法f1
现在:
a0调用f1方法,一直没有执行完
a1调用f0方法,一直没有执行完
导致a0和a1线程都在等待对象释放方法,对方都不释放,这样就形成了线程的死锁
线程通信
public class Main
{
@Test
void test()
{
Account account1 = new Account();
Account account2 = new Account();
//现在虽然还是用一个对象调用drawing方法,但是传入的是不同的对象,即对应了不同的锁
//线程1
Thread t1=new Thread(()->account1.drawing(800,account1),"微信");
//线程2
Thread t2=new Thread(()->account1.drawing(800,account1),"支付宝");
t1.start();
t2.start();
}
}
class Account
{
//账户余额
static private Integer money=1000;
//取款
public void drawing(Integer num,Account account) {
synchronized (account)//如果两个线程的对象相同,那么就会让线程阻塞,挨个执行,否则同步执行,互不干扰
{
if(Thread.currentThread().getName().equals("微信"))
{
try {
//让当前线程进入等待状态
account.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else if(Thread.currentThread().getName().equals("支付宝"))
{
//唤醒等待中的线程中优先级最高者
account.notify();
}
if(money<num)
{
System.out.println("当前余额不足,无法完成取款操作");
return;
}
System.out.println("当前取款方式:"+Thread.currentThread().getName());
System.out.println("取出"+num+"钱");
money-=num;
System.out.println("剩余:"+money+"钱");
}
}
}
注意java中所有对象,都默认有wait,notify,notifyall这三个方法
生产者和消费者
代码演示:
public class Main
{
//生产者和消费者的代码演示
@Test
public void test()
{
Clerk c=new Clerk();
//生产者
new Thread(()->{
synchronized (c)
{
while(true)//无限循环代表无限的生产次数
{
//当产品数量为0的时候,开始生产
if(c.productNum==0)
{
System.out.println("产品数量为0,开始生产");
while(c.productNum<4)
{
c.productNum++;//生成中
System.out.println("库存:"+c.productNum+"件");
}
System.out.println("当前产品数量:"+c.productNum+" 结束生产");
//唤醒消费者来消费产品
c.notify();
}
else
{
try {
//等待消费者消费完产品
c.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
//消费者
new Thread(()->{
synchronized (c)
{
while(true)//无限循环代表无限的消费次数
{
//当产品数量为4的时候,开始消费
if(c.productNum==4)
{
System.out.println("产品数量为4,开始消费");
while(c.productNum>0)
{
c.productNum--;//消费中
System.out.println("库存:"+c.productNum+"件");
}
System.out.println("当前产品数量:"+c.productNum+" 结束消费");
//唤醒生成者来生产产品
c.notify();
}
else
{
try {
//等待生产者生产完产品
c.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
//职员类
class Clerk
{
//产品数量
public static Integer productNum=0;
}
万字图解Java多线程
同步代码块,同步函数,静态同步函数的锁对象
同步代码块:
可以自己任意指定同步锁对象,同样可以指定锁的是类的字节码文件对象
同步函数:
同步函数的锁对象是固定的this对象
静态同步函数:
静态同步函数的锁对象是该函数所属的类的字节码文件对象