文章目录
多线程
多线程的概念
多线程是指同时存在几个执行体,按几条不同的执行路线共同工作的情况。
线程的概念
程序是对数据描述与操作的一段静态代码的有序集合,是应用程序执行的脚本。进程是程序的一次执行过程,它对应从代码加载、执行到执行完毕的一个完整过程。系统运行一个程序就是一个从创建、运行到消亡的过程。进程是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的一个独立单位。
线程是比进程更小的运行单位,是能独立运行的基本单位,也是能独立调度和分派的基本单位。一个进程在执行的过程中,可以产生多个线程,并且这些线程之间可以并发执行,甚至允许在一个进程中所有的县城都能并发执行。
Thread类和Runnable借口
Java中创建线程的方法有两种:一是通过继承线程类Thread来创建线程,二是建立一个实现Runnable接口的类。
主线程与线程的生命周期
每个java程序都有一个默认的主线程,对于应用程序来说,主线程是main()方法执行的线程,对于小应用程序来说,主线程指挥浏览器加载并执行JavaApplet。要想实现多线程,必须在主线程中创建新的线程对象。java语言,使用Thread类及其子类的对象来实现线程,新建的线程都有一个完整的生命周期。
(1)创建状态。即通过线程子类的构造方法创建线程对象,或者通过实现Runnable接口类对象创建一个新的线程。
(2)就绪状态。线程被创建以后,将进入线程的队列排队,如果cpu资源空闲,他就可以脱离创建他的主线程独立,另外,被阻塞的线程解除阻塞以后也进入了就绪状态。
(3)运行状态。当就绪状态的线程被调度,并且有cpu资源可以利用时,该线程就进入了运行状态,(4)挂起状态。一个正在运行的线程A可能因为某种原因被阻塞,就需要挂起,把资源让给级别高的的线程,直到cpu资源有空闲时,线程A的阻塞被解除,才能继续运行。
(5)终止状态。当一个线程正常运行完成了他的全部工作,或者被强制结束,它的生命就结束了,这时的线程处于终止状态。
线程在各个状态之间的转化及线程生命周期的演进是由系统运行状况,同时存在的其他线程和线程本身的算法共同决定的,在创建和使用线程时应注意利用现成的方法宏观的控制这个过程。
线程状态控制方法
(1)挂起一个线程
(2)停止线程
(3)线程休眠
(4)停暂线程
(5)中断线程
(6)连接线程
synchronized关键字及线程同步
有时运行一些县城需要共享数据,例如,两个线程,同时存取一个数据流,其中一个对数据进行了修改,而另一个线程使用的是原来的数据,这就带来了数据不一致的问题,如果多线程同时操作一个对象,则称该对象不是线程安全的,为了使多线程机制能够正常运转,需要采取一些措施来防止两个线程访问相同资源的冲突,特别是关键的时期,为了防止出现这样的冲突,需要在线程使用一个资源时为其加锁。访问资源的第一个线程加上锁以后,其他线程就不能再使用这个资源了,除非第一个线程被解锁。
对一种特殊资源,java提供了内建的机制来防止他们的冲突,这种机制就是对相关的方法使用关键字synchronized。
为了协调和同步线程对共享数据的操作,java.lang.Object类中提供了wait()方法和notify()方法实现线程的同步,这两个方法始终在循环中使用,不带参数的wait()方法一直等待,单位为毫秒,达到参数指定的时间后,线程又成为了就绪状态,如果没有达到指定的时间,则需要其他程序调用notify()方法将其唤醒。
使用wait()方法和notify()方法,同时配合条件检查可以有效控制多线程之间的运行顺序,实现线程之间的同步。
代码及成果展示
demo01
package demo01;
/*
*主线程 执行main()方法的线程
* 单线程程序:只有一个线程在执行,从头到尾
*
* */
public class Demo01MainThread {
public static void main(String[] args) {
Person p1=new Person("小袁");
p1.run();
//System.out.println(0/0);
Person p2=new Person("小宝");
p2.run();
}
}
package demo01;
/*
* java.long.Thread
*
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.在Thread子类中重写run方法。设置线程任务(干什么)
* 3.创建Thread子类的对象
* 4.调用Thread类中的方法start来启动线程,执行run方法
*
* */
public class Demo01Thread {
public static void main(String[] args) {
//3.创建Thread子类的对象
MyThread mt =new MyThread();
//4.调用start方法
mt.start();
for(int i =0;i<20;i++) {
System.out.println("主线程:"+i);
//if(i==10);
//System.out.println("子线程:"+i);
}
}
}
package demo01;
//1.创建thread子类
public class MyThread extends Thread {
//2.重写run方法
@Override
public void run() {
for(int i =0;i<20;i++) {
System.out.println("子线程:"+i);
}
}
}
package demo01;
public class Person {
private String name;
public Person(String name) {
this.name=name;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void run() {
for(int i=0;i<20;i++)
{
System.out.println(name+"-->"+i);
}
}
}
demo02
package demo02;
/*
* java.long.Thread
*
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.在Thread子类中重写run方法。设置线程任务(干什么)
* 3.创建Thread子类的对象
* 4.调用Thread类中的方法start来启动线程,执行run方法
*
* */
public class Demo01Thread {
public static void main(String[] args) {
//3.创建Thread子类的对象
MyThread mt =new MyThread();
//4.调用start方法
mt.start();//子线程0
new MyThread().start();//子线程1
new MyThread().start();//子线程2
new MyThread().start();//子线程3
System.out.println("main"+Thread.currentThread().getName());
}
}
package demo02;
public class Demo02ThreadSetName {
public static void main(String[] args) {
MyThreadName mt =new MyThreadName("小华");
mt.start();
new MyThreadName("小花花").start();
}
}
package demo02;
//1.创建thread子类
public class MyThread extends Thread {
@Override
public void run() {
//String name=getName();
//System.out.println("run:"+name);
System.out.println("子"+Thread.currentThread().getName());
}
}
package demo02;
public class MyThreadName extends Thread {
public MyThreadName() {}
public MyThreadName(String name) {
super(name);//把线程的名字传递给父类,让父类Thread给子线程取一个名字
}
@Override
public void run() {
//String name=getName();
//System.out.println("run:"+name);
System.out.println("子"+Thread.currentThread().getName());
}
}
demo03
package demo03;
public class Demo03Sleep {
public static void main (String[] args) {
for(int i=1;i<60;i++) {
System.out.println(i);
try {
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
demo04
package demo04;
/*
* 实现runnable接口创建多线程的好处:
* 1.避免了单继承的局限性
* 2.增强了程序的扩展性,降低了程序的耦合性
* */
public class Demo04Runnable {
public static void main(String[] args) {
//3.创建一个runnable接口的实现类 对象
//RunnableImpl run= new RunnableImpl();
//4.创建Thread类对象,构造方法中传递Runnable接口实现类对象
//Thread t=new Thread(run);
//5.调用Thread类中的start方法,启动子线程
Thread t=new Thread(new RunableImpl2());
t.start();
for (int i=0;i<20;i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
package demo04;
public class RunableImpl2 implements Runnable{
@Override
public void run() {
for(int i =0;i<20;i++) {
System.out.println("HelloWorld-->"+i);
}
}
}
package demo04;
//1.创建一个runnable接口的实现类
public class RunnableImpl implements Runnable {
//2.在实现类中 重写runnable中的run方法,设置线程任务
@Override
public void run() {
for(int i=0;i<20;i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
demo05
package demo05;
public class Demo01Ticket {
public static void main (String [] args) {
RunnableImpl run =new RunnableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
package demo05;
/*
* 解决线程安全问题的一种方案:使用同步代码块
* 格式:
* synchronized(锁对象){
* 可能会出现线程安全的代码(是因为访问了共享的数据)
* }
* 注意:
* 1.通过代码中的锁对象,可以使用任意的对象
* 2.但是必须保证多个线程使用的锁对象是统一的
* 3.锁对象作用:
* 把同步代码块锁住,只让一个线程执行
* */
public class RunnableImpl implements Runnable {
private int ticket =100;
Object obj=new Object();
@Override
public void run() {
while(true) {
synchronized(obj){
if(ticket>0) {
try {
Thread.sleep(10);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
}