Java线程

Java线程

 

线程相关名词概念:程序、进程、线程;

 程序:一行一行指令,存在电脑硬盘上的固定代码;

 进程:将硬盘上的程序调入内存执行时所创建的,即正在运行的程序

 线程:进程执行中的执行个体单元,当本进程中的所有线程单元全部执行完 毕时进程才算执行完成,然后程序运行结束;

*******************************************************************************

程序和进程关系:1:1(运行一个程序便会创建一个进程)1:n(同一程序可以在同一时间被多次打开执行,相互之间无关系)1:0(程序未调入内存,即程序还未被打开运行)

进程和线程关系:1:1(一个进程可以只有一个线程,单线程)1:n(通常一个进程对应多个线程,即多线程程序);但是一个进程一定会有一个线程,我们之前所写的所有的程序都是单线程(main函数); 

*******************************************************************************

线程的创建及使用方式:

1、继承Thread:

创建:创建一个类继承Thread类,然后重写里面的run()方法;

使用:通过创建此类的对象,然后调用此对象的start()方法;

class A extends Thread{

public void run(){

........

}

}

A a=new A();   a.start();

2、实现Runnable接口

创建:创建一个类实现Runnable接口,然后实现其run()方法;

使用:通过创建此类的对象,然后再创建一个Thread类对象,在其构造函数中将实现runnable的类对象当做参数传进去,包装成一个线程,然后调Thread类对象的start()方法;

class A  implement Runnable{

Public void run(){

..........

}

}      A a=new A(); Thread t=new Thread(a);  t.start();

3、匿名内部类的方式:

创建即启动:直接在一个类的方法中创建一个Thread类的对象,只不过无对象名,然后重写其run()方法;

new Thread(){

Public void run(){

......

}

}.start();

先创建,后启动:先在类的方法中创建Thread对象(不匿名),重写其run()方法,然后在你想启动的地方调用它的start()方法;

Thread t=new Thread(){

Public void run(){

........

}

};    t.start();

三种方式的合适选择:

1. 当已经继承了别的类了时,已经不能再继承Thread类,只能选择实现Runable接口的方式;

2. 当不需要再继承其他类的时候,毫不犹豫选择继承Thread类,因为使用方便,不需要包装;

3. 线程逻辑很简单的,即需要线程做的东西很少时,可以选择用匿名内部类方式做;

*******************************************************************************

二、线程同步(通信)问题:

1Synchronized关键字:

案例:

 张三和李四:是两夫妻

 事情:他们在中国银行开了一个户头,张三拿了银行卡,李四拿了存折,存了5000 块进去了

发生:某一天两个人没商量情况下,张拿着银行卡去了ATM机上准备去3000,正在 这个时候,李四她也拿着存着,去柜台取钱,准备取4000

 

由于银行卡与存折都能取钱,所以在这里存折与银行卡分别代表两个线程,当两个线程同时都去取钱时,就可能会出现异常情况;

 

解决:使用Synchronized关键字;

Synchronized:同步锁关键字(1.可以锁住方法   2.可以锁住指定的某个代码块);

作用:当某线程执行到被Synchronized修饰的方法或者同步代码块的时候,如果这个方法或代码块正在被其他线程操作,则此线程会阻塞,直到正在执行被 Synchronized修饰的方法或代码块的线程执行完Synchronized修饰的方法或代码块后才能进入执行,在即被Synchronized修饰的方法或者代码块在同一时刻只能被一个线程操作;

注意:Synchronized关键字在锁代码块时,需要传递一个参数,此参数可以使任何 对象类型,它代表被多个线程所共用的东西,即临界资源,加上Synchronized关键 字就是为了锁住它,让它在同一时刻只能被一个线程所访问,所以此参数必须是所 有线程在执行过程中所共用的东西;可参考生产消费者模型代码

*******************************************************************************

2、wait()notify()方法的使用

案例:生产者消费者模型;代码如下

/**

 * 生产线程:生产产品(Phone),存入容器中

 * @author minGe_000

 *

 */

public class ProducteThread extends Thread{

private ArrayList<Phone> list;

public ProducteThread(ArrayList<Phone> list){

this.list = list;

}

@Override

public void run() {

int index = 1;

while(true){

synchronized (list) {

//检测容器当中是否还有产品,如果有产品,则不生产

if(list.size()>0){

try {

//阻塞代码

list.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//生产一个产品放到容器当中

Phone phone = new Phone(index+"号手机");

index++;

list.add(phone);

System.out.println("生产者生产了一个手机,型号是:"+phone.getName());

//赶紧通知消费来取产品

list.notify();

}

}

}

}

 

/**

 * 消费线程:从容器当中,去除产品对象

 * @author minGe_000

 *

 */

public class CustomerThread extends Thread{

private ArrayList<Phone> list;

public CustomerThread(ArrayList<Phone> list){

this.list = list;

}

@Override

public void run() {

while(true){

synchronized (list) {

//检测容器当中是否还有产品,如果没有产品,则继续循环检测

if(list.size()==0){

try {

list.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//从容器当中取出一个产品

Phone phone = list.remove(0);

System.out.println("消费者消费了一个手机,型号是:"+phone.getName());

list.notify();

}

}

}

}

 

/**

 * 产品类

 * @author minGe_000

 *

 */

public class Phone {

private String name;

public Phone(String name){

this.name = name;

}

public String getName(){

return name;

}

}

 

模型解析:生产者线程负责生产产品然后把产品装在容器中,消费者线程负责消费产品即从 容器中拿出产品,所以此模型相当于一个取一个拿,装产品的容器在这里属于临界资源, 即会被所有进程访问,在两个线程执行过程中,都会对此容器进行操作,所以我们把此 容器当做参数给Synchronized关键字,然后把所有对临界资源(在这里就是产品容器)进 行操作的代码放在Synchronized关键字里面,我们假设此容器只能装一个产品,当生产 者线程发现容器中有产品的时候,便会调用wait()(必须是同步锁的参数调用)方法进行 等待,直到被同步锁参数调用nodify()方法唤醒,在进入等待的同时会释放同步锁即允 许其他线程进入临界区,当消费者线程进入临界区,取走产品之后,便使用同步锁参数 调用nodify()方法唤醒等待的生产者线程,对于消费者线程发现容器中为空而进入等待 的情况与之相似;

注意:

1waitnotify必须在同步锁之内(即被Synchronized关键字锁住的代码块或方法)使用

   2)同步锁锁定对象(即同步锁参数)和调用waitnotify对象必须同一个

   3)当对象wait挂起状态时候是会释放同步锁的

*******************************************************************************

三、线程池:

线程都有生命周期,一个线程的生命周期就是从创建到死亡的过程

 

但是创建线程的开销很大,若是任务是连续不断地,然后每次一来任务就创建出相应个数线程来执行任务,这样的开销是很大的,也很浪费资源,我们可以一下子创建出指定个数的线程存起来,定义一个容器来存取任务,当容器为空即无任务时便让线程wait()等待,容器不为空即有任务过来便nodify()唤醒线程相应个数进程执行任务,任务全部执行完后,由于已经没有任务,线程便会全部进入wait()状态,线程池就是实现这样的功能;

模型图示:

 

 

任务类:

public class MyTask implements Runnable{

public String name;

public MyTask(String name){

this.name = name;

}

public void work(){

System.out.println(name+"开始执行任务");

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(name+"任务执行完毕");

}

 

public void run() {

work();

}

}

 

线程类:

import java.util.LinkedList;

 

/**

 * 执行线程

 * @author minGe_000

 *

 */

public class Worker extends Thread{

private String name;

//一定要先获取到任务队列

private LinkedList<MyTask> tasklist;

public Worker(String name,LinkedList<MyTask> tasklist) {

this.name = name;

this.tasklist = tasklist;

}

public void run() {

while(true){

MyTask task;

synchronized (tasklist) {

if(tasklist.size()==0){

try {

tasklist.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//从任务队列中,取出一个任务,并执行这个人

task = tasklist.removeFirst();

}

System.out.println(name+"正在执行:"+task.name);

task.work();

System.out.println(name+"执行完毕:"+task.name);

}

}

}

 

线程池

import java.util.LinkedList;

 

/**

 * 线程池类

 * @author minGe_000

 *

 */

public class MyThreadPool {

//定义两个容器:一个装执行线程,一个装任务

private Worker[] workers;

private LinkedList<MyTask> taskList = new LinkedList<MyTask>();

public MyThreadPool(int size){

workers = new Worker[size];

//往数组中初始化多个执行线程对象

for (int i = 0; i < workers.length; i++) {

//创建对象

Worker worker = new Worker(i+"号执行线程",taskList);

worker.start();

workers[i] = worker;

}

System.out.println("==================线程池已经初始化好了=================");

}

//定义一个函数

public void getTask(MyTask task){

synchronized (taskList) {

taskList.add(task);

taskList.notify();

}

}

}

 

主函数

public class Test {

public static void main(String[] args) {

MyThreadPool mtp = new MyThreadPool(5);

// ExecutorService es = Executors.newFixedThreadPool(5);

for (int i = 0; i < 20; i++) {

MyTask task = new MyTask(i+"号任务");

// es.execute(task);

}

}

}

 

 

 

Java内置线程池类:

 

线程常用方法(注意配合API使用,因为有些方法已经过时)

currentThread() 返回当前运行的Thread对象。

wstop() 使调用它的线程立即停止执行,永远不要调用这个方法。

wsuspend() 使线程挂起,暂停运行。

wresume() 恢复挂起的线程,使其处于可运行状态(Runnable)。

wyield() CPU控制权主动移交到下一个可运行线程。

wsetPriority() 设置线程优先级。

wgetPriority() 返回线程优先级。

wsetName()  设置线程的名字。

wgetName() 返回该线程的名字。

wisAlive( ) 如果线程已被启动并且未被终止,那么isAlive( )返回true。如果返回false

 

HYDY编写

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值