目录
多线程
基本概念
创建线程(两种方法)
方法一:创建线程类
方法二:实现Runnable接口
多线程原理:
线程安全问题(同步)
同步代码块
同步方法
Lock锁
线程状态
TIMED_WAITING:计时等待
Waiting(无限等待)
多线程
基本概念
并行与并发
- 并发:指两个或多个事件在同一个时间段内发生。
- 并行:指两个或多个事件在同一时刻发生(同时发生)???
线程与进程的关系
进程是程序的一次执行过程,也是系统运行程序的基本单位
线程是进程中的一个执行单元,负责当前进程中程序的执行,1个进程中至少有1个线程
一个程序运行后至少有一个进程,一个进程中可以包含多个线程
线程的调度
- 分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 - 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
JVM执行main方法,main会进入栈内存;JVM会找操作系统开辟一条main方法通向cpu的执行路径,cpu就可以通过这个路径来执行main方法 ;这个路径就是主线程
创建线程(两种方法)
方法一:创建线程类
自定义类继承Thread类,重写run方法
public class Mythread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("线程"+i);
}
}
}
创建线程对象并且启动线程
new Mythread().start();
start()方法会自动调用run方法
方法二:实现Runnable接口
public class test1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程");
}
}
}
创建线程对象并且启动线程
new Thread(new test1()).start();
使用匿名内部类的方式实现:
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程");
}
}
}).start();
Thread类的常用方法
- public String getName() :获取当前线程名称。
- public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
- public void run() :此线程要执行的任务在此处定义代码。
- public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
- public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
多线程原理:
?当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
实现Runnable接口比继承Thread类所具有的优势: 1. 适合多个相同的程序代码的线程去共享同一个资源。 2. 可以避免java中的单继承的局限性。 3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。 4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
线程安全问题(同步)
示例代码:(模拟卖票:同时开启三个线程)
public class Mythread extends Thread{
public Mythread() {
}
public Mythread(String name) {
super(name);
}
private int num=100;
@Override
public void run() {
while (true){
if(num>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+num);
num--;
}
}
}
}
问题分析:
1、售出3张100 :线程执行if后会进入休眠,放弃cpu的使用权,替他的线程会抢占资源
2、出现1、0、-1的情况:线程唤醒后(已经进入了if语句体),直接输出num--的数据
同步代码块
同步锁:对象锁,多个对象要是有同一把锁(类似于 一把钥匙)(在run方法外定义)
public class Mythread extends Thread{
private static int num=100;//共享资源 需要定义为静态资源
//将同步锁 设定为成员属性 建立对象时,将object赋值给它(以保证 多个线程使用一把同步锁)
private Object object;
public Mythread() {
}
public Mythread(String name ,Object object) {
super(name);
this.object=object;
}
@Override
public void run() {
while (true){
synchronized (object){
if(num>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+num);
num--;
}
}
}
}
}
测试代码:
public static void main(String[] args) {
Object o = new Object();
new Mythread("售票口1",o).start();
new Mythread("售票口2",o).start();
new Mythread("售票口3",o).start();
}
同步方法
同步锁是谁? 对于非static方法,同步锁就是this。 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
将可能会存在线程安全问题的代码抽取到方法中
注意:同步锁是this,所以最好使用实现接口的方式创建线程,给Thread传入的Runnable接口实现类对象必须一样
示例代码:
public class test1 implements Runnable{
private static int num=100;
@Override
public void run() {
while (true){
method();
}
}
public synchronized void method(){
if(num>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
}
}
测试代码:
public static void main(String[] args) {
test1 test1 = new test1();
new Thread(test1,"A").start();
new Thread(test1,"B").start();
new Thread(test1,"C").start();
new Thread(test1,"D").start();
}
Lock锁
同步锁:默认是this
因为同步锁是this,所以最好使用实现接口的方式创建线程
相关方法:
- public void lock() :加同步锁。
- public void unlock() :释放同步锁。
public class test1 implements Runnable{
private static int num=100;
Lock lock=new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
if(num>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
lock.unlock();
}
}
}
线程状态
NEW:新建?????????????????RUNNABLE:可运行? ? ? ? ? ? ? ? BLOCKED:阻塞? ? ? ? ? ? ? ? ?WAITING:无限等待? ? ? ? ? ? ? ? TIMED_WAITING:计时等待? ? ? ? ? ? ? ? TERMINATED:被终止
TIMED_WAITING:计时等待
方法一:Thread.sleep(10)
方法二:Object.wait(毫秒值)(时间一到,自动唤醒)
Waiting(无限等待)
Object.wait:无限等待(只能等待唤醒)
Object.notify()/Object.notifyAll():唤醒单个或多个线程
示例代码(生产者、消费者) 两个线程必须使用同一把锁
public static void main(String[] args) {
Object o=new Object();//同步锁
//消费者
new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (o){
System.out.println(Thread.currentThread().getName()+"点餐");
try {
o.wait();//无限等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("吃完了");
}
}}
}).start();
//生产者
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(5000);//作用让消费者先点餐
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o){
System.out.println("做好了");
o.notify();//唤醒等待线程
}
}
}
}).start();
}
|