多线程的基本概念_程序_进程_线程
一、
程序:指令的集合
进程:正在执行的程序是一个静态的概念,进程是程序的一次静态执行的过程,占用特定的地址空间。
每个进程都是独立的,由cpu,data,code三部分组成。
缺点:内存的浪费,cpu的负担。
线程:进程中的一个单一的连续控制流程,轻量级进程。
一个进程可以有多个并行的线程
一个进程中的线程共享相同的内存单元,地址,空间。可以访问相同的变量和对象。
从同一个堆中分配对象
由于线程间的通信是在同一地址进行,所以不需要额外的通信机制,使得通信更简便,更快速。
二、
进程与线程的区别
一个进程中包含了多个线程,线程结束进程不一定结束。
进程结束,线程都结束。
cpu的调度执行的就是线程。
多线程的实现两种方式
通过继承Thread类实现多线程
1、继承Thread类
2、重写run()方法
3、通过start方法启动线程
package cn.xjion.pro11;
/**
* mian线程和mt线程会并行,一起执行
* @author xjion
*/
public class MyThread extends Thread{
// 重写run方法
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("Thread---"+i);
}
}
public static void main(String[] args) {
// 创建线程类的对象
MyThread mt = new MyThread();
// 启动线程
mt.start();
// main线程的for循环
for (int i = 0; i < 10; i++) {
System.out.println("main---"+i);
}
}
}
一定的缺点:Java中的类是单继承,一旦继承了Thread类就没办法继承其他类了。
实现Runnable接口实现多线程
1、编写类实现Runnable接口
2、实现run()方法
3、通过Thread类的start()方法启动线程
package cn.xjion.pro11;
/**
* 通过继承Runnable接口实现线程的并行
* @author xjion
*/
public class MyRunnable implements Runnable{
// 重写run方法
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("Run---"+i);
}
}
public static void main(String[] args) {
// 创建实现Runnable接口的类的对象
MyRunnable mr = new MyRunnable();
// 创建线程对象thread传入实现Runnable的类
Thread thread = new Thread(mr);
// 创建Thread线程,传入创建线程类的对象
// Thread thread = new Thread(new MyRunnable());
// 通过调用Thread中的start方法开启线程
thread.start();
// main线程的循环
for (int i = 0; i < 10; i++) {
System.out.println("main---"+i);
}
}
}
静态代理模式
Thread --> 代理角色
MyRunnable --> 真是角色
代理角色与真实角色实现共同的接口Runnable接口
package cn.xjion.pro11;
public interface Marry {
void marry();
}
package cn.xjion.pro11;
public class You implements Marry{
@Override
public void marry() {
// TODO Auto-generated method stub
System.out.println("结婚咯,好开心啊");
}
}
package cn.xjion.pro11;
public class MarryCompany implements Marry{
//重写marry方法
@Override
public void marry() {
// TODO Auto-generated method stub
// 婚前
this.before();
// m结婚
m.marry();
// 婚后
this.after();
}
// 私有结婚对象
private Marry m;
// 构造器
public MarryCompany(Marry m) {
super();
this.m = m;
}
public void after(){
System.out.println("收拾东西,非常开心!!!");
}
public void before(){
System.out.println("婚前准备,非常激动!!!");
}
}
package cn.xjion.pro11;
public class TestMarry {
public static void main(String[] args) {
// 创建真实角色
Marry m = new You();
// 创建代理角色
Marry myc = new MarryCompany(m);
// 调用结婚
myc.marry();
}
}
线程的状态
新生状态
用new关键字建立一个线程对象后,改线程对象就处于新生状态
处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态
就绪状态
具备了运行条件。但还没有被分配到cpu,一旦获得cpu,线程就进入运行状态并自动调用run方法
导致线程进入就绪的四种方法:
1. 新建线程:调用start()方法,进入就绪状态;
2. 阻塞线程:阻塞解除,进入就绪状态;
3. 运行线程:调用yield()方法,直接进入就绪状态;
4. 运行线程:JVM将CPU资源从本线程切换到其他线程。
运行状态
线程执行自己的run方法,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡
阻塞状态
阻塞是暂停线程执行以等待某个条件发生
导致阻塞的四种原因
1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。
3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。
死亡状态
两种情况:
1、正常结束,完成了全部工作结束。
2、被强制终止,如调用stop()或destroy()方法来终止一个线程,但不推荐使用
当线程死亡了就不能再进入其他状态了。
package cn.xjion.pro11;
/**
* 终止线程的典型方法
* @author xjion
*/
public class TestThreadCiycle implements Runnable{
String name;
// 用来判断线程什么时候结束
boolean live = true;
// 构造器
public TestThreadCiycle(String name) {
super();
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
while(live){
System.out.println(name+(i++));
}
}
public void terminate(){
live = false;
}
public static void main(String[] args) {
TestThreadCiycle ttc = new TestThreadCiycle("线程1:");
// 新生态
Thread thread = new Thread(ttc);
// 就绪态
thread.start();
// 循环打印main线程
for (int i = 0; i < 10; i++) {
System.out.println("main线程---"+i);
}
// 调用方法把live赋值false
ttc.terminate();
System.out.println("ttc停止运行");
}
}
获取线程基本信息的方法
常用方法
序号 | 方法名称 | 描述 |
1 | static Thread currentThread() | 返回目前正在执行的线程 |
2 | final String getName() | 返回线程的名称 |
3 | final boolean isAlive() | 判断线程是否处于活动状态 |
package cn.xjion.pro11;
public class TestThreadMethod implements Runnable{
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println(t);
}
public static void main(String[] args) {
Thread t = Thread.currentThread();
//toString()方法得到的内容为[线程名称,线程的优先级,线程组的名称]
System.out.println(t);
// 创建对象
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my);
Thread t2 = new Thread(my);
Thread t3 = new Thread(my);
// 线程是同时开启,并行的
t1.start();
t2.start();
t3.start();
/**在Thread类中一定有一个静态变量int,用于统计创建线程的个数*/
//线程的默认命名规则 Thread - int类型的变量值
}
}
package cn.xjion.pro11;
public class TestGetName {
public static void main(String[] args) {
//主线程的名称
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println("主线程的名称" + name);
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my,"自定义线程1");
Thread t2 = new Thread(my,"线程2");
Thread t3 = new Thread(my,"线程3");
System.out.println(t3.getName());
t1.start();
t2.start();
t3.start();
}
}
package cn.xjion.pro11;
public class TestAlive extends Thread{
@Override
public void run() {
for (int i = 0;i < 10;i++) {
System.out.println(Thread.currentThread().getName() + "--------------->" + i);
}
}
public static void main(String[] args) {
//主线程
MyThread my = new MyThread();
System.out.println("新生状态的线程是否处于活动状态:"+ my.isAlive());
my.start();//启动线程
System.out.println("线程my处于就绪状态的线程是否处于活动状态:" + my.isAlive());
//主线程中的循环
for (int i = 0;i < 10;i++) {
System.out.println("----------"+Thread.currentThread().getName()+"--------->"+i);
}
//主线程中的最后一句代码
System.out.println("my线程是否处于活动状态:" + my.isAlive());
}
}
多线程的安全性问题
package cn.xjion.pro11;
public class Ticket implements Runnable{
private int ticket = 5;
@Override
public void run() {
for (int i=0;i<100;i++) {//每个窗口排了100个人
if (ticket > 0) {//有票
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第" + (ticket--)+"张票");
}
}
}
}
package cn.xjion.pro11;
public class TestTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket,"A窗口");
Thread t2 = new Thread(ticket,"B窗口");
Thread t3 = new Thread(ticket,"C窗口");
t1.start();
t2.start();
t3.start();
}
}
暂停线程执行
sleep_yield_join_stop
暂停的方法
方法名称 | 描述 |
final void join() | 调用该方法的线程强制执行,其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行 |
static void sleep(long millis) | 使用当前正在执行的线程休眠millis秒,线程处于阻塞状态 |
static void yield() | 当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行 |
final void stop() | 强迫线程停止执行。已过时。不推荐使用 |
package cn.xjion.pro11;
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
TestJoin tj = new TestJoin();
Thread t = new Thread(tj);
Thread t2 = new Thread(tj);
t.start();
t2.start();
for (int i = 0;i < 10;i++) {
if (i == 3) {
t2.join();
}
System.out.println("------------"+Thread.currentThread().getName()+"-------->"+i);
}
}
/**
* 导致调用它的线程进入阻塞状态,而不会导致其它的进程
* */
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i < 10;i++) {
System.out.println(Thread.currentThread().getName()+"------>"+i);
}
}
}
package cn.xjion.pro11;
public class TestSleep2 implements Runnable{
/**
* sleep方法会导致线程进入阻塞,写哪个线程体中就会导致哪个线程进入阻塞状态
* @throws InterruptedException
* */
public static void main(String[] args){
TestSleep2 ts = new TestSleep2();
new Thread(ts).start();
System.out.println("主线程开始休眠");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程休眠结束");
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println("MyThread2.run(),线程开始休眠");
Thread.sleep(3000);
System.out.println("MyThread2.run(),休眠结束");
} catch (InterruptedException e) {
System.out.println("MyThread2.run(),产生异常");
}
}
}
package cn.xjion.pro11;
public class TestYield implements Runnable{
public static void main(String[] args) {
TestYield ty = new TestYield();
new Thread(ty).start();
for (int i = 0;i < 10;i++) {
if (i == 5) {
Thread.yield();//主线程礼让一次
System.out.println(Thread.currentThread().getName()+"线程礼让一次");
}
System.out.println(Thread.currentThread().getName()+"------------"+i);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0;i < 10;i++) {
if (i == 3) {
Thread.yield();
System.out.println("当前线程:"+Thread.currentThread().getName()+"线程礼让一次");
}
System.out.println("i="+i);
}
}
}
package cn.xjion.pro11;
public class TestStop implements Runnable{
public static void main(String[] args) {
TestStop ts = new TestStop();
Thread t = new Thread(ts);
t.start();
for (int i = 0;i<10;i++) {
if (i==3) {
t.stop(); //已过时,不建议使用
}
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0;i < 10;i++) {
System.out.println("i="+i);
}
}
}
总结
1.sleep
不会释放锁,Sleep时别的线程也不可以访问锁定对象。
2.yield
让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
3.join
当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。
4.stop
直接强制结束线程
线程优先级
方法
方法名称 | 描述 |
final int getPriority() | 获取线程的优先级 |
final void setPriority(int priority) | 设置线程的优先级 |
package cn.xjion.pro11;
public class Test implements Runnable{
public static void main(String[] args) {
System.out.println("最高优先级:" + Thread.MAX_PRIORITY);
System.out.println("最低优先级:" + Thread.MIN_PRIORITY);
System.out.println("默认优先级" + Thread.NORM_PRIORITY);
//主线程的优先级
Thread t = Thread.currentThread();
System.out.println("获取主线程的优先级:" + t.getPriority());
Thread t2 = new Thread(new MyThread());
System.out.println("新建的线程优先级为:" + t2.getPriority());
/**
* 优先级越高越有可能先被调用执行,但是不一定
*/
t2.setPriority(6);
System.out.println("t2线程的优先级:" + t2.getPriority());
//t2.setPriority(100); //非法参数,因为优先级只能是1-10之间的整数
}
@Override
public void run() {
// TODO Auto-generated method stub
}
}
线程同步
通过synchronized使线程同步
锁方法:public synchronized void method(){},锁的是调用当前方法对象里面的方法
锁语句块:synchronized(对象){},锁的是传入的当前对象
package cn.xjion.pro11;
public class Ticket3 implements Runnable{
private int ticket = 5;
@Override
public void run() {
for (int i=0;i<100;i++) {//每个窗口排了100个人
this.saleTicket();
}
}
private synchronized void saleTicket() { //无需指定同步监视器,同步监视器只能是当前对象this
if (ticket > 0) {//有票
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第" + (ticket--)+"张票");
}
}
}
死锁及解决办法
死锁产生原因
多线程操作时,互相等待对方的资源
package cn.xjion.pro11;
public class DeadLock extends Thread{
private Object money;
private Object water;
public boolean flag;//标识持有对象锁
public DeadLock(Object money, Object water) {
super();
this.money = money;
this.water = water;
}
public void run() {
if (flag) {//true时,持有“钱”的锁
synchronized (money) {
System.out.println("有钱,等水");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (water) {
System.out.println("有水,等钱");
}
}
}else {
synchronized (water) {
System.out.println("有水,等钱");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (money) {
System.out.println("有钱,等水");
}
}
}
}
}
解决死锁
package cn.xjion.pro11;
public class DeadLock2 extends Thread{
private Object money;
private Object water;
public boolean flag;//标识持有对象锁
public DeadLock2(Object money, Object water) {
super();
this.money = money;
this.water = water;
}
public void run() {
if (flag) {//true时,持有“钱”的锁
synchronized (money) {
System.out.println("有钱,等水");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synchronized (water) {
System.out.println("有水,等钱");
}
}else {
synchronized (water) {
System.out.println("有水,等钱");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synchronized (money) {
System.out.println("有钱,等水");
}
}
}
}
避免死锁
银行家算法: 该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。(计算资源的大小,计算出来后,永远按照从大到小的方式来获得锁)。
多线程通信
线程通信的目的是为了能够让线程之间相互发送信号。另外,线程通信还能够使得线程等待其它线程的信号,比如,线程B可以等待线程A的信号,这个信号可以是线程A已经处理完成的信号;
Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题。
- * wait():释放当前线程的同步监视控制器,并让当前线程进入阻塞状态,直到别的线程发出notify将该线程唤醒。
- * notify():唤醒在等待控制监视器的其中一个线程(随机)。只有当前线程释放了同步监视器锁(调用wait)之后,被唤醒的线程才有机会执行。
- * notifyAll():与上面notify的区别是同时唤醒多个等待线程。
值得注意的是这三个方法是属于Object而不是属于Thread的,但是调用的时候必须用同步监视器来调用,wait(), notify(), notifyAll() 必须和synchronized关键字联合使用
package cn.xjion.pro14;
import java.util.ArrayList;
import java.util.List;
public class ListAdd {
private volatile static List list = new ArrayList<>();
public void add(){
list.add("xjion");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd list1 = new ListAdd();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
list1.add();
System.out.println("当前线程:"+Thread.currentThread().getName()+"添加了一个元素");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while(true){
if (list1.size() == 5) {
System.out.println("当前线程收到通知:"+Thread.currentThread().getName()+"list size = 5 线程停止..");
throw new RuntimeException();
}
}
}
},"t2");
t1.start();
t2.start();
}
}
package cn.xjion.pro14;
import java.util.concurrent.CountDownLatch;
import cn.xjion.list.ArrayList;
import cn.xjion.list.List;
public class ListAdd2 {
private volatile static List list = new ArrayList();
public void add(){
list.add("xjion");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd2 list2 = new ListAdd2();
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
//synchronized (lock) {
for(int i = 0; i <10; i++){
list2.add();
System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
Thread.sleep(500);
if(list2.size() == 5){
System.out.println("已经发出通知..");
countDownLatch.countDown();
//lock.notify();
}
}
//}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//synchronized (lock) {
if(list2.size() != 5){
try {
//System.out.println("t2进入...");
//lock.wait();
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
throw new RuntimeException();
//}
}
}, "t2");
t2.start();
t1.start();
}
}