多线程
常见概念
线程与进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
线程调度
分为分时调度和抢占式调度,java使用的是抢占式调度,每个线程拥有优先级,互相竞争cpu使用权
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
同步与异步
同步:排队执行 , 效率低但是安全.
异步:同时执行 , 效率高但是数据不安全.
并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
实现多线程的两个方法
Tread继承
重写run方法,然后在需要的地方创建实例并调用start方法开始线程。
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程使用了");
}
}
实现Runable
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("线程使用了");
}
}
实现Runable与继承Thread相比的优势
1.通过创建任务,然后给线程分配实现更适合各个线程同时执行相同任务的情况.
2.可以避免单继承的局限性
3.任务与线程本身是分离的,提高程序健壮性
4线程池接受Runable类型的任务不接受Thread类型,操作更方便
常用方法
变量和类型 | 方法 | 描述 |
---|---|---|
static Thread | currentThread() | 返回对当前正在执行的线程对象的引用。 |
long | getId() | 返回此Thread的标识符。 |
String | getName() | 返回此线程的名称 |
static Thread | currentThread() | 返回对当前正在执行的线程对象的引用。 |
int | getPriority() | 返回此线程的优先级。 |
void | interrupt() | 中断此线程。 |
void | setDaemon(boolean on) | 将此线程标记为 daemon线程或用户线程。 |
void | setName(String name) | 将此线程的名称更改为等于参数 name 。 |
void | setPriority(int newPriority) | 更改此线程的优先级。 |
static void | sleep(long millis) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 |
static void | sleep(long millis, int nanos) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。 |
void | start() | 导致此线程开始执行; Java虚拟机调用此线程的run方法。 |
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread t = new Thread(myThread);
t.setName("守护线程:");
t.setDaemon(true);
t.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
线程安全
线程安全问题
指多个线程同时执行一段逻辑时,判断的数据可能和执行前的数据发生变化,造成结果错误
public class Test {
public static void main(String[] args) {
MyThread run = new MyThread();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
}
class MyThread implements Runnable {
int count = 10;
@Override
public void run() {
while (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
--count;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
}
}
}
三种解决方案
同步代码块
用synchronized(Object object){要同步的代码块} 例如上例中的问题代码
public class Test {
public static void main(String[] args) {
MyThread run = new MyThread();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
}
class MyThread implements Runnable {
Object o = new Object();
int count = 10;
@Override
public void run() {
while (true) {
synchronized (o) {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
--count;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
} else {
break;
}
}
}
}
}
同步方法
使用synchronized关键字修饰方法
public class Test {
public static void main(String[] args) {
MyThread run = new MyThread();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
}
class MyThread implements Runnable {
Object o = new Object();
int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag) {
break;
}
}
}
public synchronized boolean sale() {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
--count;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
} else {
return false;
}
return true;
}
}
显式锁Lock
声明一个ReentrantLock对象,在要同步的代码开头使用lock()方法,结尾处使用unLock()方法:
public class Test {
public static void main(String[] args) {
MyThread run = new MyThread();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
}
class MyThread implements Runnable {
Object o = new Object();
int count = 10;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
--count;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
} else {
break;
}
l.unlock();
}
}
}
线程死锁
两个线程互相访问同步的方法或资源时,会造成线程死锁
在开发时,如果一个线程已经有了一个锁,则不要调用另一个可能产生锁的对象或方法