多线程
并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一个时刻发生(同时发生)。
进程与线程
进程:指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。进入到内存中的程序。
线程:线程是进程的一个执行的单位,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程 中是可以有多个线程的,这个应用程序也可以称为多线程程序。
简之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
Thread
-
public class Thread
extends Object
implements Runnable线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为
main
的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:-
已经调用了
Runtime
类的exit
方法,并且安全管理器已经允许进行退出操作。 -
所有不是守护进程线程的线程都已经死亡,无论是从调用返回到
run
方法还是抛出超出run
方法的run
。
创建一个新的执行线程有两种方法。 一个是将一个类声明为
Thread
的子类。 这个子类应该重写run
类的方法Thread
。 然后可以分配并启动子类的实例。 例如,计算大于规定值的素数的线程可以写成如下:class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeThread p = new PrimeThread(143); p.start();
另一种方法来创建一个线程是声明实现类
Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。 这种其他风格的同一个例子如下所示:class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将
null
参数传递给null
中的构造函数或方法将导致抛出NullPointerException
。 -
public class Thread01 extends Thread{
private String name;
public Thread01(String name) {
this.name = name;
}
public Thread01() {
}
public class Thread02 implements Runnable {
private Integer age;
public Thread02() {
}
public Thread02(Integer age) {
this.age = age;
}
public class ThreadMain {
public static void main(String[] args) {
Thread01 haha = new Thread01("haha");
haha.start();
Thread02 thread02 = new Thread02(18);
new Thread(thread02).start();
for (int i = 0; i < 20; i++) {
System.out.println("main"+i);
}
}
}
常用方法:
-
-
getName() 返回此线程的名称。
-
run()
如果这个线程使用单独的
Runnable运行对象构造,则调用该
Runnable对象的
run`方法; 否则,此方法不执行任何操作并返回。 -
sleep(long millis)` 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
-
start()
导致此线程开始执行; Java虚拟机调用此线程的
run方法。 -
currentThread() 返回对当前正在执行的线程对象的引用。
-
/**
* @program: intellij idea
* @description:定义一个thread子类
* @author: lixy
* @create: 2020-05-04 21:18
**/
public class MyThread01 extends Thread {
@Override
public void run() {//重写run方法
String name = getName();
System.out.println("MyThread01线程名称:"+name);
Thread thread = MyThread01.currentThread();
System.out.println(thread);
System.out.println(Thread.currentThread().getName());
}
}
public class Demo02Sleep {
public static void main(String[] args) {
for (int i = 1; i <= 60; i++) {
System.out.println(i);
//使用thread类的sleep方法让程序睡眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口创建多线程的好处:
1、避免了单继承的局限性
一个类只能继承一个类,类继承了Thread类就不能继承其他的类
实现Runnable接口,还可以继承其他的类,实现其他的接口。
2、增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
实现类中,重写了run方法,用来设置线程任务
创建Thread类对象,调用start方法,用来开启新线程。
线程同步
方法:
1、同步代码块
2、同步方法
3、锁机制
/**
* @program: intellij idea
* @description:实现卖票
* @author: lixy
* @create: 2020-05-04 22:02
**/
public class RunnableImpl implements Runnable {
private static ArrayList<String> tickets = new ArrayList<String>(Arrays.asList("1号","2号","3号","4号","5号","6号","7号","8号"));
//创建一个锁对象
private Object obj = new Object();
//3-1、在成员位置创建一个
private Lock lock = new ReentrantLock();
//设置线程任务:卖票
@Override
public void run() {
//1同步代码块
/*synchronized (obj){
//先判断票是否存在
if (tickets.size()>0){
int i = new Random().nextInt(tickets.size());
String ticket = tickets.get(i);
if (tickets.contains(ticket)){
tickets.remove(ticket);
System.out.println(Thread.currentThread().getName()+"——存在:"+ticket);
}
}
}*/
//2、同步方法
/*metho();*/
//3、锁机制Lock锁
lock.lock();//3-2枷锁
try {
if (tickets.size()>0){
int i = new Random().nextInt(tickets.size());
String ticket = tickets.get(i);
if (tickets.contains(ticket)){
tickets.remove(ticket);
System.out.println(Thread.currentThread().getName()+"——存在:"+ticket);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//3-3解锁
}
}
public /*static*/ synchronized void metho(){//静态同步方法
if (tickets.size()>0){
int i = new Random().nextInt(tickets.size());
String ticket = tickets.get(i);
if (tickets.contains(ticket)){
tickets.remove(ticket);
System.out.println(Thread.currentThread().getName()+"——存在:"+ticket);
}
}
}
}
Wait(等待)与Notify(唤醒)
-
-
void
notify()
唤醒正在等待对象监视器的单个线程。void
notifyAll()
唤醒正在等待对象监视器的所有线程。String
toString()
返回对象的字符串表示形式。void
wait()
导致当前线程等待,直到另一个线程调用该对象的notify()
方法或notifyAll()
方法。void
wait(long timeout)
导致当前线程等待,直到另一个线程调用notify()
方法或该对象的notifyAll()
方法,或者指定的时间已过。void
wait(long timeout, int nanos)
导致当前线程等待,直到另一个线程调用该对象的notify()
方法或notifyAll()
方法,或者某些其他线程中断当前线程,或一定量的实时时间。
-
public class Demo04WaitAndNotify {
public static void main(String[] args) {
//锁对象
Object o = new Object();
new Thread(){