目录
区分程序,进程,线程
- 程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码.
- 进程((process):正在内存中运行的应用程序,如运行中的QQ,运行中的音乐播放器。进程是操作系统进行资源分配的最小单位.
- 线程(thread):进程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程。
注意:
一个进程可以包含多个线程.
一个线程只能属于一个进程,线程不能脱离进程而独立运行.
每一个进程至少包含一个线程(称为主线程).
在主线程中可以创建并启动其它的线程.
一个进程内的所有线程共享该进程的内存资源.
如何创建线程?
1、继承Thread类
用Thread类创建线程: 1、写一个类继承java.lang.Thread 2、重写run() 线程中要执行的任务都要写在run()中,或者在run()中进行调用 调用时用(类.start)调用 注意: run():这不是启动线程,只是一个方法的调用,没有启动线程,还是单线程模式
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("MyThread"+i);
}
}
public class ThreadTest {
public static void main(String[] args) {
//创建并启动线程
MyThread myThread=new MyThread();
//myThread.run();这不是启动线程,只是一个方法的调用,没有启动线程,还是单线程模式
myThread.start();//启动线程
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
Thread类中的方法
run(); 用来定义线程要执行的任务代码 start(); 启动线程的方法 currentThread(); 获取到当前线程 getId(); 获取线程的唯一标识,id setName(); 为线程设置名字.|getName();| sleep();让线程阻塞休眠指定的时间 join()方法:等待调用了join线程执行完毕,其他线程再执行。 Thread.yield();放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上就获得CPU时间片。 setPriority();设置线程的优先级。|getPriority();| getState()方法:获取当前线程的状态.
public class TreadTest {
public static void main(String[] args) {
MyThread myThread=new MyThread();
System.out.println(myThread.getState());//NEW
myThread.setName("站点1");
myThread.setPriority(1);
myThread.start();
MyThread myThread1=new MyThread();//RUNNABLE
System.out.println(myThread.getState());
System.out.println(myThread1.getState());//NEW
myThread1.setName("站点2");
myThread1.setPriority(10);
myThread1.start();//开始线程
System.out.println(myThread1.getState());
System.out.println("--------");
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println("----");
}
}
}
public class MyThread extends Thread{
public void run(){
System.out.println(Thread.currentThread());
System.out.println(Thread.currentThread().getName()+":线程id:"+Thread.currentThread().getId()+",线程优先级"+Thread.currentThread().getPriority()+",线程状态"+Thread.currentThread().getState());
}
}
2、实现Runnable接口
方法:创建一个类,实现Runnable接口,重写任务执行的run()。 用Runnable接口来创建线程: 1、只先创建任务 2、再创建线程new Thread(指定执行的任务)。 3、调用thread.start,让线程去执行
public class Test implements Runnable{
@Override
public void run() {
test();
}
public void test(){
for (int i = 0; i < 1000; i++) {
System.out.println("自定义线程"+i);
}
}
public class TestTest2 {
public static void main(String[] args) {
//方式2:Test类实现Runnable接口,重写run()
Test test=new Test();
//只先创建任务,在创建线程new Thread(指定执行的任务),调用thread.start,让线程去执行
Thread thread=new Thread(test);
thread.start();
//方式1:MyThread类继承Thread,重写run()
MyThread myThread=new MyThread();
myThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
Runnable接口创建的优点:
1.因为java是单继承,一旦级承一个类就不能在继承其他类,避免单继承的局限。 2.适合多线程来处理同一份资源时使用。
线程的优先级
事实上,计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务;
优先级较高的线程有更多获得CPU的机会,反之亦然;
优先级用整数表示,取值范围是1~10。
一般情况下,线程的默认优先级都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级。
Thread类有如下3个静态常量来表示优先级
●MAX_PRIORITY:取值为10,表示最高优先级。
●MIN_PRIORITY:取值为1,表示最底优先级。
●NORM_PRIORITY:取值为5,表示默认的优先级。
线程状态
1、新建:
当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
2、就绪:
处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源。
3、运行:
当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能。
4、阻塞:
在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。
5、死亡:
线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。
线程状态图:
![](https://img-blog.csdnimg.cn/direct/b7945720286f42a4b2c2f125299a2401.png)
多线程概念
多线程是指:程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
当程序需要同时执行两个或多个任务。
当程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
使用多线程完成
多线程优缺点:
优点:
- 提高程序的响应
- 提高CPU的利用率
- 改善程序结构,将复杂任务分为多个线程,独立运行
缺点:
- 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
- 多线程需要协调和管理,所以需要跟踪管理线程,使得cpu开销变大;
- 线程之间同时对共享资源的访问会相互影响,如果不加以控制会导致数据出错
多线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制,即各线程间要有先来后到;
确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个线程获取了这把锁,才有权利访问该共享资源。
同步就是:排队+锁;
线索安全问题:
如何枷锁:
synchronized关键字:
修饰方法(会自动提供 非静态方法锁对象是this,静态方法锁对象是当前类的class类的对象)
修饰代码块(提供同步锁对象 必须唯一static)
ReentrantLock类:
修饰代码块
1、同步锁(synchronized):
同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用来充当锁标记).
同步执行过程:
1.第一个线程访问,锁定同步对象,执行其中代码.
2.第二个线程访问,发现同步对象被锁定,无法访问.
3.第一个线程访问完毕,解锁同步对象.
4.第二个线程访问,发现同步对象没有锁,然后锁定并访问.
synchronized修饰方法
synchronized修饰方法时,同步锁对象不需要我们指定 同步锁对象会默认提供: 1:非静态的方法--锁对象默认是this(如果一个类中有两个对象) 2:静态方法-----锁对象是当前类的Class对象(类的对象,一个类的对象只有一个)
package D5继承Thread;
public class Synchronized2 extends Thread{
/*
class被加载到内存里都会创建一个class的对象,就将类的信息封装在class类中
synchronized修饰方法时,同步锁对象不需要我们指定
同步锁对象会默认提供:
1:非静态的方法--锁对象默认是this(如果一个类中有两个对象)
2:静态方法-----锁对象是当前类的Class对象(类的对象,一个类的对象只有一个)
*/
static int num=10;
@Override
public void run() {
while(true) {
if (num <= 0) {
break;
}
this.print();
}
}
public static synchronized void print(){
if(num>0){
System.out.println(Thread.currentThread().getName()+"买到了第"+num+"张票");
num--;
}
}
}
package D5继承Thread;
public class SyncTest {
public static void main(String[] args) {
Synchronized2 sync=new Synchronized2();
sync.setName("窗口1");
sync.start();
Synchronized2 sync2=new Synchronized2();
sync2.setName("窗口2");
sync2.start();
}
}
synchronized修饰对象:
synchronized (同步锁对象){ 同步代码块 } 同步锁对象作用:用来记录有没有线程进入到同步代码块中,如果有线程进入到同步代码块,直到上一个线程执行完同步代码块的内容,释放锁之后,其他线程才能进入 同步锁对象要求:同步锁对象必须是唯一的(多个线程拿到的是同一个对象).
package D5继承Thread;
public class Ticket extends Thread {
static int ticket = 10;
static Object object=new Object();
@Override
public void run() {
while (true) {
synchronized (object) {// synchronized 关键字:加锁。作用:有没有线程进入到同步代码块。要求:多个线程对应同一个同步锁对象static
if (ticket > 0) {
try {
Ticket.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Ticket.currentThread().getName() + "抢到了第" + ticket + "张票");
ticket--;
} else
break;
}
}
}
}
package D5继承Thread;
public class TicketTest {
public static void main(String[] args) {
Ticket ticket=new Ticket();
ticket.setName("窗口1");
ticket.start();
Ticket ticket1=new Ticket();
ticket1.setName("窗口2");
ticket1.start();
}
}
2、Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制。
通过显式定义同步锁对象来实现同步。
同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,可以显式加锁释放锁。
线程交替网络
sleep(long time): 属于Thread类中的方法 sleep休眠指定的时间后,会自动唤醒 sleep()不会释放锁wait(): 属于Object类中的方法,必须要用锁对象调用 wait后的线程,必须通过其他线程唤醒(notify(),notifyAll()) wait()会自动释放锁
public class Counter {
int num=1;
public static void main(String[] args) {
//创建了一个对象,用synchronized锁,锁对象是this
Counter counter=new Counter();
Customer customer=new Customer(counter);
customer.start();
Productor productor=new Productor(counter);
productor.start();
}
//生产者调用 添加商品
public synchronized void add() throws InterruptedException {//添加商品
if(num==0){
this.notify();//唤醒消费者
num++;
System.out.println("生产者线程生产了一件商品");
}else {//商品不能为空,进入等待
this.wait();
}
}
//消费者调用 拿走商品
public synchronized void sub() throws InterruptedException {
if(num>0){
this.notify();
num--;
System.out.println("消费者取走了一件商品");
}else {
this.wait();//没有商品,进入等待
}
}
}
public class Customer extends Thread{
Counter counter;
public Customer(Counter counter) {
this.counter=counter;
}
@Override
public void run() {
while (true){//消费者线程一直消费
try {
this.counter.add();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Productor extends Thread{
Counter counter;
public Productor(Counter counter) {
this.counter=counter;
}
@Override
public void run() {
while (true){
try {
this.counter.sub();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}