Java基础day07—线程
1、进程:运行中的应用程序,每个进程都有自己独立的地址空间(内存空间)eg.点击一个桌面的浏览器,操作系统为该进程分配一个独立的内存空间,当用户再次点击时,又启动一个进程,操作系统将为新的进程分配新的独立的内存空间。
线程:进程中的一个实体,是被系统独立调度和分派的基本单位,线程不拥有系统资源,只拥有一点在运行中必不可少的资源,但他可与同属于一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发进行。线程有五个基本状态:新建、就绪、运行、阻塞、死亡。
只要应用程序涉及到并发,就离不开多线程编程
1)线程是轻量级的进程 2)线程没有独立的线程空间 3)线程是由进程创建的(寄生在进程)4)一个进程可以有多个线程(多线程编程)5)五种状态
一个类当作线程来使用的两种方法:1)继承Thread类,并重写run函数 2)实现Runnable接口,并重写run函数
线程–继承Thread和实现Runnable的区别:
1)尽可能使用实现Runnable接口的方式来创建线程
2)在使用Thread时只需new一个实例。调用start()即可启动一个线程
3)在使用Runnable时需先new一个实现Runnable的实例,之后再用Thread调用
/*
作者:mys
功能:继承Thread类的方式使用线程
日期:2018/7/21
*/
public class Demo5 {
public static void main(String []args){
Cat1 cat=new Cat1();
//启动线程,导致run函数的运行
cat.start();
}
}
class Cat1 extends Thread{
int times=0;
//重写run函数
public void run(){
while(true) {
try {
//休眠1s 1000表示1ms
//sleep让线程进入到Blocked状态,并释放资源
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
times++;
System.out.println("Hello word");
if(times==10) {
break;
}
}
}
}
/*
功能:实现Runnable的方式使用线程
*/
public class Demo5_1 {
public static void main(String []args){
//注意启动
Dog dog=new Dog();
//创建一个Thread对象
Thread thread=new Thread(dog);
thread.start();
}
}
/*
功能:两个线程同时进行
*/
public class Demo6 {
public static void main(String []args){
//创建两个对象
Monkey mon=new Monkey(10);
Pig pig=new Pig(10);
//创建两个Thread对象
Thread t1=new Thread(mon);
Thread t2=new Thread(pig);
//启动线程
t1.start();
t2.start();
}
}
//输出
class Pig implements Runnable {
int n;
int times = 0;
int sum = 0;
public Pig(int n) {
this.n = n;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是一个线程,正在输出第" + times + "个Hello word");
times++;
if (times == n) {
break;
}
}
}
}
//计算
class Monkey implements Runnable {
int n;
int times = 0;
int sum = 0;
public Monkey(int n) {
this.n = n;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum += (++times);
System.out.println("当前值为:" + sum);
if (times == n) {
//System.out.println("最终值为:" + sum);
break;
}
}
}
}
2、线程对象只能启动一个线程
java线程的同步问题:多线程并发有很多好处,完成更有效的程序,但是也有很多安全问题,如下例子:
/*
功能:模拟机票售票系统,三个售票点在一天内卖出2000张票,
注意是一共卖出2000张,而不是每个卖出2000张
日期:2018/7/22
问题:出票线程是连续的
*/
public class Demo8 {
public static void main(String []args){
//创建一个窗口
TicketWindows tw=new TicketWindows();
//三个线程同时启动
Thread t1=new Thread(tw);
Thread t2=new Thread(tw);
Thread t3=new Thread(tw);
t1.start();
t2.start();
t3.start();
}
}
//售票窗口类
class TicketWindows implements Runnable{
private int ticket=2000;
@Override
public void run() {
while (true) {
//先判断是否还有票
if (ticket > 0) {
//显示售票信息,Thread.currentThread().getName()获取线程名字
System.out.println(Thread.currentThread().getName() + "正在售票第" + ticket + " 张");
try {
//每秒售出一张
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
} else {
//售票结束
break;
}
}
}
}
问题:出现同一张票被三个售卖点同时售卖
解决问题的关键:保证容易出问题的代码的原子性,原子性是指当a线程在执行某代码时,别的线程必须等待其执行完后,才能执行它的代码;只需在需要同步的代码段用synchronized(Object){你要同步的代码}
,因此在上述代码修改如下:
while(true){
//if else要保证其原子性[同步代码块]
synchronized(this){
if(){
}
}
}
保证了每张票只有一个售卖点在售出
同步机制:
1)Java任意对象都有一个标志,该标志具有0、1两种状态,开始状态为1,当某个线程执行synchronized时,对象标志位为0,直到执行完个块代码块后,该标志位又回到1的状态。
2)当一个线程执行到synchronized语句时,先判断标志位,如果为0,则这个线程将暂时阻塞,让出cpu资源,直到另外的线程完成相关同步代码,并将Object标志为1,这个线程的阻塞暂时被取消,线程才继续运行,并将该线程的标志位变为0,防止其他代码块进入相关同步代码。
3)如果有多个线程因同时等待同一个对象的标志位而处于阻塞状态时,当该对象的标志位恢复到1时,只能有一个线程能够进入同步代码执行。
对象的标志位就是对象锁