Java多线程:
进程:进行中的程序
线程:就是进程中一个负责程序执行的控制单元(执行单元)
一个进程中可以多执行路径,称之为多线程
一个进程至少一个线程
开启多个线程是为了同时运行多部分代码
每个线程都有自己运行的内容,这个内容成为线程要执行的任务
多线程的好处:解决了多部分同时运行的问题
多线程的弊端:线程太多回到效率的降低
其实应用程序的执行都是CPU在做着快速的切换完成的,这个切换是随机的
JVM启动时就启动了多个线程,至少有两个线程
1.执行main函数的线程
该线程的任务代码都定义在main函数里
2.负责垃圾回收的线程
如何创建一个线程:
方式一:继承Thread类
步骤:
1.创建一个类继承Thread类
2.重写Thread的run方法//重写run方法
3.直接创建Thread类的子类对象C创建线程
4.调用start方法开启线程并调用线程的任务run方法执行
class Demo extends Thread
{
private String name = "弟鸽";
Demo(String name)
{
super(name);
//this.name = name;
}
public void run(){
for(int i=1;i<10;i++)
{
for(long j= -199999999;j<199999999;j++){ }
system.out.println(name+"*****"+i+"--------"
+Thread.currentThread().getName());
}
}
}
main(){
Demo d1 = new Demo("脔割");
Demo d2 = new Demo("儿纸");
//d1.run();
//d2.run();
d1.start();
d2.start();
}
可以通过Thread的getName获取线程的名称 Thread—(0开始)
主线程的名称是 main
创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码同时运行
而运行的指定代码就是这个执行路径的任务
JV创建的主线程的任务都定义在主函数里
自定义的线程的任务在run方法里;
Thread类用于描述线程,线程是 需要任务的,所以Thread类也对任务的描述,这个任务就通过Thread类中的run方法实现。也就是说,run方法就是封装自定义线程运行任务的函数。
run方法中定义的就是线程要运行的任务代码
开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法,将要运行的代码定义在run方法中即可。
run()和start()的区别:
1.start()可以启动一个新的线程
2.start()不能重复调用run()可以
3.start()中的run()代码可以不执行完就继续执行下面的代码,即进行了线程切换。直接调用run()方法必须等待其代码全部执行完才能继续执行下面的代码
4.start()实现了多线程,run()没有实现多线程
临时阻塞状态 具备执行资格但不具备执行权 正在等待执行权
↑ ↑
↓ ↑
进程--->start()--->运行--->sleep(time)--->冻结//释放执行权的同时
| ---> wait() --->↑ 释放执行资格
↓ <--- notify() <---↑
stop()
|
↓
消亡
cpu执行资格: 可以被cpu处理,在处理队列中排队
cpu的执行权: 正在被cpu处理
创建线程的第二种方法:
1.定义类实现Runnable接口
2.覆盖接口种的run方法,将线程的任务代码封装到run方法中
3.通过Tread类创建对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。(原因是线程任务都封装在Runnable接口子类对象的run方法中,所以在线程对象创建时就得明确要运行的任务。)
4.调用线程对象的start方法开启线程
class Demo2 implements Runnable //准备扩展Demo2类的功能,让其中的内容作为线程的任务执行
//通过接口的形式完成
{
public void run()
{
show();
}
public void show(){
for(int i=1;i<10;i++)
{
for(long j= -199999999;j<199999999;j++){}
System.out.println(Thread.currentThread().getName());
}
}
}
main()
{
Demo2 d3 = new Demo2();
Thread t1 = new Thread(d3,"办证");
Thread t2 = new Thread(d3,"学妹介绍Q");
t1.start();
t2.start();
}
Runnable接口:将线程的任务进行了对象的封装
实现Runnable接口的好处:
1.将线程的任务从线程的子类中分离出来,进行了单独的封装
按照面向对象的思想将任务的封装成对象
2.避免了Java单继承的局限性
故较为常用的是实现Runnable
线程安全问题产生的原因:
1.多个线程在操作共享的数据
2.操作共享数据的代码有多条
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算
就会导致线程安全问题的产生
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,必须要当成线程把这些代码都执行完毕后,其他线程才可以 ------->局部代码块
在Java中用同步代码块就可以解决这个问题
synchronized(对象)
{
局部代码;
}
同步的好处:
解决了线程的安全问题
同步的弊端:
相对降低了效率,因为同步外的线程都会判断同步锁。
同步的前提:
同步中必须有多个线程吗,并使用同一个锁。
同步函数的锁是this
同步函数和同步代码块的区别:
1.同步函数的锁是固定的this
同步代码块的锁是任意的对象
建议使用同步代码块
当同步函数为static时,锁为this.getClass()即该函数所属字节码文件对象,可用getClass()方法获取,也可以用当前
类名.class 表示。
死锁:
class DeadLockTestDemo implements Runnable
{
private boolean flag;
DeadLockTestDemo(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--If--->LockA");
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--If--->LockB");
}
}
}
else
{
while(true)
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--Else--->LockB");
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--Else--->LockA");
}
}
}
}
}
class MyLock
{
public static final Object lockA = new Object();
public static final Object lockB = new Object();
}
public class DeakLockTest {
public static void main(String[] args) {
DeadLockTestDemo dlt1 = new DeadLockTestDemo(true);
DeadLockTestDemo dlt2 = new DeadLockTestDemo(false);
Thread t1 = new Thread(dlt1);
Thread t2 = new Thread(dlt2);
t1.start();
t2.start();
}
}
进程间的通信:
等待/唤醒机制
1.wait(): 让cpu处于冻结状态,被wait的线程会被存储到线程池中
2.notify(): 唤醒线程池中的一个线程(任意)
3.botifyAll():唤醒线程池中的所有线程
class Resource
{
String name;
int age;
boolean flag = false;
}
class Input implements Runnable
{
Resource r;
Input(Resource r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(x==0)
{
r.name = "脔割";
r.age = 18;
}
else
{
r.name = "弟鸽";
r.age = 17;
}
r.flag = true;
r.notify();
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
Resource r = new Resource();
Output(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
synchronized(r)
{
if(!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(r.name+"--->"+r.age);
r.flag = false;
r.notify();
}
}
}
public class ResourceDemo {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
线程的wait();notify();notifuAll()定义在Oblect类中的原因是:
因为这些方法是监视器的方法,监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中